Security 10 min read

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.

Matt King
Matt King
April 28, 2026
Last updated: April 28, 2026
Open Ports in Production: Why Your Laravel Server Has More Exposed Services Than You Think

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 SET command
  • 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:

  1. Check for default credentials: MySQL root with no password, Redis with no AUTH.
  2. Search for known CVEs: A specific MySQL or Redis version might have a public exploit.
  3. Attempt protocol-specific attacks: Redis CONFIG SET for file writes, MySQL LOAD DATA LOCAL INFILE for file reads.
  4. Chain with other findings: An exposed Redis instance combined with a leaked .env file (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.

Stay Updated on Laravel Security

Get actionable security tips, vulnerability alerts, and best practices for Laravel apps.