Open Ports in Production: Why Your Laravel Server Has More Exposed Services Than You Think
Most Laravel deployments expose far more network services than developers realise. From MySQL and Redis to forgotten Vite dev servers, open ports give attackers a roadmap to your infrastructure. Here is how to find and close them.
Every server connected to the internet communicates through ports. When you deploy a Laravel application, port 443 serves your HTTPS traffic and port 80 handles HTTP redirects. Those are intentional. The problem is what else is listening.
Most Laravel production servers expose more services than their developers realise. A MySQL instance bound to all interfaces. A Redis server with no authentication accepting connections from anywhere. A Vite development server that was never shut down. Each of these open ports is a door, and every door an attacker can see is a door they will try to open.
This post walks through why open ports matter, which services are the most common culprits in Laravel deployments, how attackers find and exploit them, and what you can do to lock things down.
What open ports actually are
A port is a numbered endpoint (0 through 65,535) that a service binds to in order to accept network connections. When Nginx listens on port 443, it is telling the operating system: "send me any TCP traffic that arrives on this port."
A port is considered "open" when a service is actively listening on it and the server's firewall allows inbound connections to reach it. A port is "closed" when no service is listening, and "filtered" when a firewall drops or rejects the traffic before it reaches the service.
From a security perspective, every open port is part of your attack surface. The service behind it might have:
- Known vulnerabilities (unpatched software)
- Default or weak credentials
- Misconfigured access controls
- Information disclosure through banners or error messages
The goal is simple: only expose the ports that absolutely need to be public, and lock everything else down.
Common culprits in Laravel deployments
Laravel applications depend on several backing services. In development, it is convenient to run everything on the same machine with permissive configurations. Those configurations have a habit of following the application into production.
MySQL on port 3306
MySQL is the default database for most Laravel projects. In development, your .env file typically contains:
DB_HOST=127.0.0.1
DB_PORT=3306
The application connects over localhost, and that is fine. The problem arises when MySQL itself is configured to bind to 0.0.0.0 (all interfaces) in the MySQL configuration file:
# /etc/mysql/mysql.conf.d/mysqld.cnf
bind-address = 0.0.0.0
With this setting, MySQL accepts connections from any IP address. If your firewall does not block port 3306, your database is directly accessible from the internet. Attackers will find it within hours.
Redis on port 6379
Redis is used in Laravel for caching, sessions, and queues. By default, older versions of Redis ship with no authentication and bind to all interfaces. This is one of the most exploited misconfigurations on the internet.
An attacker connecting to an unprotected Redis instance can:
- Read and modify all cached data, including session tokens
- Flush the cache, causing a denial-of-service condition
- Write arbitrary files to the server using the
CONFIG SETcommand - In some configurations, achieve full remote code execution
Redis 7.x defaults to binding to localhost only, but many production servers still run older versions, or have their configuration overridden.
Vite development server on port 5173
Laravel projects using Vite for asset compilation run a development server during local development. This server listens on port 5173 by default. If a developer runs npm run dev on a production server for debugging purposes, or if a Docker container is configured to start the Vite dev server alongside the production application, port 5173 becomes publicly accessible.
The Vite dev server is not designed for production use. It can expose source maps, raw source files, and internal file paths. In some configurations, it enables hot module replacement (HMR) over WebSocket, which can be manipulated.
Other services worth checking
- Memcached (port 11211): No authentication by default. Used in amplification DDoS attacks.
- PostgreSQL (port 5432): Similar risks to MySQL when bound to all interfaces.
- Laravel Horizon dashboard: Often runs on the main web port but exposed without authentication.
- Node.js debug port (port 9229): If Node processes are started with
--inspect, this port allows remote debugging, including code execution. - Mailpit/MailHog (ports 1025, 8025): Development mail catchers that occasionally end up running in production.
- phpMyAdmin or Adminer: Web-based database tools typically running on port 8080 or similar.
How attackers enumerate and exploit open ports
Port scanning is one of the first steps in any reconnaissance operation. Attackers do not need sophisticated tools or deep technical knowledge. The process is largely automated.
Mass scanning
Tools like Masscan can scan the entire IPv4 internet on a single port in under six minutes. Services like Shodan, Censys, and ZoomEye continuously scan and index every publicly accessible service on the internet. Your server is already in their database.
An attacker looking for exposed Laravel infrastructure might search Shodan for:
port:6379 product:"Redis"
port:3306 product:"MySQL"
port:5173 "vite"
These searches return thousands of results, complete with IP addresses, service versions, and sometimes banner information that reveals the operating system and application stack.
Targeted enumeration
Once an attacker identifies your server (through DNS records, certificate transparency logs, or your application itself), they will run a targeted port scan:
nmap -sV -p- --open your-server-ip
This scans all 65,535 TCP ports, identifies which are open, and attempts to determine the service and version running on each. The output might look like:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1
80/tcp open http nginx 1.24.0
443/tcp open ssl/http nginx 1.24.0
3306/tcp open mysql MySQL 8.0.36
6379/tcp open redis Redis 7.2.4
5173/tcp open http Vite dev server
Ports 22, 80, and 443 are expected. Ports 3306, 6379, and 5173 are the problem. Each one gives the attacker a specific service to target with known exploit techniques.
Exploitation
With service versions identified, the next step is straightforward:
- Check for default credentials: MySQL root with no password, Redis with no AUTH.
- Search for known CVEs: A specific MySQL or Redis version might have a public exploit.
- Attempt protocol-specific attacks: Redis
CONFIG SETfor file writes, MySQLLOAD DATA LOCAL INFILEfor file reads. - Chain with other findings: An exposed Redis instance combined with a leaked
.envfile (containing the Redis password) gives the attacker full control over your cache, sessions, and queues.
Cloud provider security groups and firewall configuration
Most Laravel applications run on cloud providers like AWS, DigitalOcean, Hetzner, or Linode. Each provider offers firewall or security group functionality, but the defaults vary and are often too permissive.
AWS Security Groups
AWS security groups are stateful firewalls attached to EC2 instances. The default outbound rule allows all traffic. Inbound rules must be explicitly added, but it is common to see:
Type: All traffic
Protocol: All
Port range: All
Source: 0.0.0.0/0
This rule allows the entire internet to reach every port on your instance. It is the most dangerous misconfiguration possible, and it usually gets created during initial setup when a developer "just wants to get things working."
A proper security group for a Laravel application should look like:
Inbound:
- TCP 443 from 0.0.0.0/0 (HTTPS)
- TCP 80 from 0.0.0.0/0 (HTTP, redirects to HTTPS)
- TCP 22 from YOUR_IP/32 (SSH, restricted to your IP)
Outbound:
- All traffic (or restrict to known destinations)
Database, cache, and other internal services should only be accessible from within your VPC or private network.
DigitalOcean Cloud Firewalls
DigitalOcean's cloud firewalls are applied at the network level, before traffic reaches the Droplet. They are free and simple to configure, but they are not enabled by default. A newly created Droplet has all ports open unless you explicitly attach a firewall.
UFW (Uncomplicated Firewall)
For servers where cloud firewalls are not available, or as an additional layer, UFW provides host-level firewalling on Ubuntu:
# Reset to deny all incoming
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow only what's needed
sudo ufw allow 443/tcp
sudo ufw allow 80/tcp
sudo ufw allow from YOUR_IP to any port 22
# Enable the firewall
sudo ufw enable
# Verify
sudo ufw status verbose
Important: Always allow SSH before enabling UFW, or you will lock yourself out of the server.
Port scanning your own infrastructure
You should be scanning your own servers before attackers do. Here are several approaches.
Using nmap from an external machine
Run nmap from a machine outside your infrastructure (your laptop, a separate VPS) to see what the internet sees:
# Quick scan of common ports
nmap -sV --top-ports 1000 your-server-ip
# Full scan of all ports (takes longer)
nmap -sV -p- your-server-ip
# Scan with script detection for deeper analysis
nmap -sV -sC -p- your-server-ip
Using netstat or ss from the server itself
To see what services are listening on the server:
# Show all listening ports with process names
sudo ss -tlnp
# Example output:
# LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=1234))
# LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234))
# LISTEN 0 151 0.0.0.0:3306 0.0.0.0:* users:(("mysqld",pid=5678))
# LISTEN 0 511 0.0.0.0:6379 0.0.0.0:* users:(("redis-server",pid=9012))
Any service listening on 0.0.0.0 is bound to all interfaces. If your firewall is not blocking that port, it is publicly accessible.
Automated external monitoring
Manual scans are useful but easy to forget. Ideally, your external attack surface is monitored continuously. StackShield runs automated external scans against your Laravel application, including port detection, service identification, and configuration checks. When a new port opens, whether from a configuration change, a service restart, or a container misconfiguration, you get alerted before an attacker finds it.
Run a free scan to see what is currently exposed on your server.
Hardening guidelines
Here is a checklist for reducing your port exposure in production.
1. Bind services to localhost
If a service only needs to be accessed from the same server, bind it to 127.0.0.1:
# MySQL: /etc/mysql/mysql.conf.d/mysqld.cnf
bind-address = 127.0.0.1
# Redis: /etc/redis/redis.conf
bind 127.0.0.1
# PostgreSQL: /etc/postgresql/16/main/postgresql.conf
listen_addresses = 'localhost'
2. Use private networks for service-to-service communication
If your database runs on a separate server from your application, use your cloud provider's private network (VPC) rather than the public internet. Configure database connections in your .env to use private IPs:
DB_HOST=10.0.1.5
REDIS_HOST=10.0.1.6
3. Enable authentication on every service
Even services bound to localhost should require authentication. This provides defence in depth:
# Redis: /etc/redis/redis.conf
requirepass your-strong-password-here
# MySQL: Ensure no accounts use empty passwords
SELECT user, host FROM mysql.user WHERE authentication_string = '';
4. Remove development tools and services
Before deploying, ensure development-only services are not running:
# Check for common development services
sudo ss -tlnp | grep -E ':(5173|8025|1025|9229|4200) '
If you use Docker, verify that your docker-compose.prod.yml does not include Vite, Mailpit, or other dev-only containers.
5. Apply firewall rules at multiple layers
Use both cloud-level firewalls (security groups) and host-level firewalls (UFW, iptables). This way, a misconfiguration in one layer does not expose your services:
- Cloud firewall: Allow only 80, 443, and restricted SSH
- Host firewall (UFW): Mirror the same rules
- Service configuration: Bind to localhost or private IPs
6. Audit regularly
Infrastructure drifts. Developers SSH in and start services for debugging. Configuration management tools revert changes. Containers get added with new port mappings. Set up a recurring process to scan your own infrastructure, or use a monitoring service like StackShield that does it for you continuously.
A real-world example
Consider a typical Laravel deployment on a single server. The developer provisions an Ubuntu VPS, installs Nginx, PHP-FPM, MySQL, and Redis, then deploys the Laravel application. During setup, they open all ports in the security group to troubleshoot a connectivity issue and forget to lock them down afterward.
Six months later, the server's open ports look like this:
| Port | Service | Risk |
|---|---|---|
| 22 | SSH | Acceptable (if key-only auth) |
| 80 | Nginx (HTTP) | Acceptable (redirects to HTTPS) |
| 443 | Nginx (HTTPS) | Acceptable |
| 3306 | MySQL | High: Database accessible from internet |
| 6379 | Redis | Critical: No authentication, all data exposed |
| 9229 | Node.js debug | Critical: Remote code execution possible |
The developer has no idea these ports are open. The application works fine. Meanwhile, automated scanners have already indexed the Redis instance, and it is only a matter of time before someone exploits it.
This is exactly the kind of exposure that StackShield detects automatically. An external scan would flag ports 3306, 6379, and 9229 immediately, before an attacker takes advantage of them.
Conclusion
Open ports are one of the most overlooked security risks in Laravel deployments. They are easy to create, easy to forget, and easy for attackers to find. The combination of permissive cloud security groups, services bound to all interfaces, and development tools left running in production creates an attack surface far larger than most teams realise.
The fix is not complicated. Bind services to localhost. Configure your firewalls properly. Remove development tools from production. Scan your own infrastructure from the outside. And set up continuous monitoring so you catch new exposures the moment they appear, not after an attacker has already exploited them.
Run a free StackShield scan to see what your server looks like from an attacker's perspective. It takes 30 seconds and covers open ports, security headers, exposed configuration files, debug mode detection, and more.
Frequently Asked Questions
What is an open port and why is it a security risk?
An open port is a network port on your server that is actively accepting connections. Each open port represents a running service, and each running service is a potential entry point for attackers. If the service behind the port has a vulnerability, is misconfigured, or uses weak credentials, an attacker can exploit it to gain access to your server or data. The fewer ports you expose to the public internet, the smaller your attack surface.
Which ports are commonly left open on Laravel servers by mistake?
The most common offenders are MySQL on port 3306, Redis on port 6379, Memcached on port 11211, the Vite development server on port 5173, and debug or profiling tools on various ports. Many of these get exposed because developers copy development configurations into production, or because cloud security groups are set to allow all inbound traffic during initial setup and never locked down afterward.
How can I scan my own server for open ports?
You can use nmap from an external machine to scan your server. Run "nmap -sV -p- your-server-ip" for a comprehensive scan of all 65,535 ports with service version detection. You can also use online tools or services like StackShield that perform external port scanning and service detection automatically on a recurring schedule, alerting you when new ports appear.
Is it safe to bind Redis and MySQL to 0.0.0.0 if I use a strong password?
No. A strong password helps, but it should never be your only line of defence. Passwords can be brute-forced, leaked, or bypassed through application-level vulnerabilities. The safest approach is defence in depth: bind services to 127.0.0.1 or a private network interface, restrict access through firewall rules, and use strong authentication. If a service does not need to be reachable from the internet, it should not be.
How does StackShield help with open port monitoring?
StackShield performs external security scans of your Laravel application from the perspective of an attacker. This includes detecting open ports, identifying the services running behind them, and flagging known risky configurations like publicly accessible database ports or development servers. It runs these checks automatically and alerts you when your exposure changes, so you catch new open ports before an attacker does.
Related Articles
Laravel Debug Mode Left On in Production? Here's What Attackers See (and How to Fix It)
APP_DEBUG=true in production exposes your database credentials, API keys, environment variables, and full stack traces to anyone who triggers an error. Here's exactly what gets leaked, how attackers find it, and the 2-minute fix.
SecurityOWASP Top 10 in Laravel: Real Vulnerabilities, Real Code Fixes (2026)
SQL injection through raw queries. XSS from unescaped Blade output. CSRF bypasses on API routes. Every OWASP Top 10 category mapped to Laravel-specific vulnerabilities with code you can copy to fix them.
SecurityIs Your Laravel .env File Exposed? How to Check and Fix It
Your .env file contains database credentials, API keys, and encryption secrets. If it's accessible from the web, attackers already have everything they need. Here's how to check and fix it.
Compare StackShield
Security Checklists
Laravel Production Deployment Security Checklist
A comprehensive security checklist for deploying Laravel applications to production. Covers environment config, server hardening, access control, and monitoring.
20 itemsLaravel API Security Checklist
Secure your Laravel API endpoints against common vulnerabilities. Covers authentication, input validation, rate limiting, and response security.
Stay Updated on Laravel Security
Get actionable security tips, vulnerability alerts, and best practices for Laravel apps.