Securing Deployments
There are many steps we can take to secure our applications. This list is in priority order, meaning the first steps are the most important and should be implemented on all our projects while later steps may only be necessary of the most secure applications.
Cloudflare Proxy
Section titled “Cloudflare Proxy”Traditionally, you would have a DNS record that points directly to your application’s web server. Using Cloudflare’s proxy service, all requests get sent to Cloudflare’s network first, then Cloudflare forwards the request to your application’s web server.
Benefits
Section titled “Benefits”Proxying requests keeps your application’s web server hidden from the public internet. This makes it harder for attackers to discover and exploit vulnerabilities and allows us to leverage Cloudflare’s advanced network security features.
Prerequisites
Section titled “Prerequisites”- You are using Cloudflare for your DNS
Implementation
Section titled “Implementation”- Navigate to the DNS Records page in Cloudflare
- Add a DNS A record pointing to the web server’s IP address and enable Cloudflare proxy via the toggle.
//TODO: Add information about setting up Trusted Proxies in laravel so that the proxy doesn’t break rate limiting
Cloudflare Origin Certificates
Section titled “Cloudflare Origin Certificates”While the Cloudflare Proxy secures the connection between the user and Cloudflare, we must also secure the connection between Cloudflare and our VPS. We use Cloudflare Origin Certificates to ensure valid encryption end-to-end without relying on complex Let’s Encrypt renewal chains on the origin.
Benefits
Section titled “Benefits”Eliminates “Man-in-the-Middle” attacks between Cloudflare’s edge and our server. It also simplifies SSL management as Origin Certificates can be issued for 15 years, removing the need for frequent automated renewals on the server.
Prerequisites
Section titled “Prerequisites”- Cloudflare Proxy enabled.
Implementation
Section titled “Implementation”- In Cloudflare Dashboard, go to SSL/TLS > Origin Server.
- Click Create Certificate. Keep default settings (RSA 2048, 15 years).
- In Laravel Forge, navigate to the Site > SSL.
- Select Install Existing Certificate and paste the Private Key and Certificate provided by Cloudflare.
- Set the Cloudflare SSL/TLS mode to Full (Strict).
Authenticated Origin Pulls (mTLS)
Section titled “Authenticated Origin Pulls (mTLS)”This is the most critical security layer for our prod environments. We configure the Nginx web server to only accept traffic that presents a specific client certificate signed by Cloudflare.
Benefits
Section titled “Benefits”This effectively “locks” the web server to the internet. Even if an attacker discovers the real IP address of the VPS, they cannot access the website because they cannot present the Cloudflare client certificate. The connection is dropped instantly.
Prerequisites
Section titled “Prerequisites”- Cloudflare Origin Certificate installed (see above).
- Authenticated Origin Pulls enabled in Cloudflare Dashboard (SSL/TLS > Origin Server).
Implementation
Section titled “Implementation”- Download the Cloudflare CA certificate to the server:
Terminal window wget https://developers.cloudflare.com/ssl/static/authenticated_origin_pull_ca.pem -O /etc/nginx/certs/cloudflare-origin-pull.pem - In Laravel Forge, edit the Nginx Configuration for the site.
- Add the following within the
server { ... }block listening on port 443:ssl_client_certificate /etc/nginx/certs/cloudflare-origin-pull.pem;ssl_verify_client on; - Save and restart Nginx.
Firewall IP Allow-listing & Tailscale (Dark SSH)
Section titled “Firewall IP Allow-listing & Tailscale (Dark SSH)”We implement a “Zero Trust” network architecture. We use iptables to block all traffic at the packet level that does not come from Cloudflare (for web traffic) or our internal Tailscale network (for management).
Benefits
Section titled “Benefits”Reduces server load by blocking bot scanners at the network level before they hit Nginx. Removes the SSH port (22) from the public internet entirely, nullifying brute-force SSH attacks/
Prerequisites
Section titled “Prerequisites”- Tailscale installed and authenticated on the VPS.
- Server tagged correctly in Tailscale Admin Panel (e.g.,
tag:prodortag:dev).
Implementation
Section titled “Implementation”1. Tailscale & SSH
- Ensure the UFW firewall allows traffic on the Tailscale interface but blocks public SSH (except for Forge):
Terminal window # Allow local loopback and Tailscale networkufw allow in on loufw allow in on tailscale0# Deny public SSH (Port 22), but allow Laravel Forge IPs# (Forge IPs must be added manually or via script)ufw deny 22/tcp
2. Web Traffic (Cloudflare IP Script)
- We run a root-level script to fetch Cloudflare IPs and update
iptablesto drop all other traffic on port 443. - This script must run via
sudo crontab -e(not the Forge scheduler) to have permission to modify firewall rules. - Note: Ensure the script includes a failsafe to allow SSH access via the Tailscale interface.
Application Hardening
Section titled “Application Hardening”We tweak the default PHP and Laravel configurations to reduce information leakage and prevent client-side attacks.
Benefits
Section titled “Benefits”Prevents “reconnaissance” where attackers determine version numbers to find exploits. Prevents XSS, Clickjacking, and MIME-sniffing attacks.
Implementation
Section titled “Implementation”1. PHP Configuration
- In Forge, go to PHP > FPM Configuration.
- Search for and update:
expose_php = Off. - Restart PHP-FPM.
2. Nginx Configuration
- In the Nginx config, ensure
server_tokens off;is set.
3. Security Headers (Laravel Middleware)
- In
bootstrap/app.php, append the Security Headers middleware. - This middleware must inject:
X-Frame-Options: DENYX-Content-Type-Options: nosniffReferrer-Policy: strict-origin-when-cross-originContent-Security-Policy(configured for Inertia/React).
Operational Security (Key Rotation)
Section titled “Operational Security (Key Rotation)”Security is a process, not just a configuration. We assume that despite our best efforts, secrets may leak.
Benefits
Section titled “Benefits”Limits the “blast radius” of a compromised API key. If a key was leaked 2 months ago, rotating it today ensures an attacker loses access immediately.
Policy
Section titled “Policy”- Frequency: Quarterly (Every 3 months).
- Scope:
- AWS IAM User Keys (S3/SES).
- Database Passwords (if accessible externally).
- Stripe/Payment Gateway Secret Keys.
- Third-party Service Tokens (Slack, Twilio, etc.).
- Protocol: Generate new key -> Update
.envon all servers -> Verify functionality -> Revoke old key.