What Is SSL/TLS?
Cryptographic protocols that provide encrypted communication between a client (browser) and server. SSL (Secure Sockets Layer) is the predecessor to TLS (Transport Layer Security). TLS 1.2 and 1.3 are the current standards. These protocols ensure data transmitted between users and your application cannot be intercepted or modified.
How It Works
TLS establishes an encrypted channel between a client and server through a multi-step handshake that authenticates the server's identity and negotiates a shared encryption key.
Step 1: ClientHello — The browser initiates the handshake by sending a ClientHello message listing: supported TLS versions (1.2, 1.3), a random 32-byte value (client_random), and a list of supported cipher suites ordered by preference. In TLS 1.3, only five cipher suites are supported, all providing forward secrecy.
Step 2: ServerHello — The server responds with: the selected TLS version (highest mutually supported), the selected cipher suite, a server_random value, and its certificate chain. The certificate chain includes the server's certificate (issued to yourapp.com) and any intermediate CA certificates.
Step 3: Certificate verification — The browser validates the certificate chain by checking: the certificate is signed by a trusted root CA (from the browser's built-in trust store), the Subject Alternative Name or Common Name matches the requested hostname, and the certificate has not expired and is not in the Certificate Revocation List.
Step 4: Key exchange — In TLS 1.3, both parties perform an ephemeral Diffie-Hellman (ECDHE) key exchange. Each side generates a temporary key pair, exchanges public keys, and independently derives the same shared secret — the session key. The private keys are never transmitted. Each session uses a unique ephemeral key, providing forward secrecy: compromising the server's long-term private key does not decrypt past sessions.
Step 5: Finished — Both sides send Finished messages encrypted with the new session key to confirm the handshake was not tampered with. All subsequent data is encrypted with AES-GCM (the dominant symmetric cipher in modern TLS) using the session key.
TLS 1.0/1.1 risk — Older protocol versions support cipher suites with known weaknesses: RC4 (BEAST attack), CBC mode without proper padding validation (POODLE attack). These must be disabled in Nginx via ssl_protocols TLSv1.2 TLSv1.3;.
Types of SSL/TLS
Expired Certificate
SSL certificate passes its validity end date — browsers display a hard-block warning with no bypass option, making the site effectively inaccessible and breaking API integrations.
# Check certificate expiration date
echo | openssl s_client -servername yourapp.com -connect yourapp.com:443 2>/dev/null \
| openssl x509 -noout -dates
# notAfter=Jan 15 00:00:00 2025 GMT <-- expired!
Weak TLS Protocol Version
TLS 1.0 or 1.1 are enabled on the server — these deprecated versions support vulnerable cipher suites (POODLE, BEAST attacks) and fail PCI DSS compliance requirements.
# Test if TLS 1.0 is incorrectly accepted:
openssl s_client -connect yourapp.com:443 -tls1 2>&1 | grep "Cipher"
# Should fail with "alert handshake failure" — if it succeeds, TLS 1.0 is enabled
Certificate Name Mismatch
Certificate was issued for a different hostname than the one being accessed — browsers reject with "certificate is not valid for this domain" and break all HTTPS connections.
# Certificate covers: www.yourapp.com
# Application accessed at: api.yourapp.com
# Result: browser error NET::ERR_CERT_COMMON_NAME_INVALID
# Fix: use wildcard cert (*.yourapp.com) or SAN covering all subdomains
In Laravel Applications
Laravel applications should enforce HTTPS in production using URL::forceScheme("https") and the HSTS header. Your SSL certificate should cover all subdomains and use TLS 1.2 or higher. Check certificate expiration dates regularly to avoid outages.
Code Examples
Nginx TLS Configuration: Weak vs. Strong
Vulnerable
# Nginx allowing deprecated TLS versions — vulnerable to POODLE/BEAST
server {
listen 443 ssl;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # VULNERABLE: old versions
ssl_ciphers HIGH:!aNULL:!MD5; # Weak cipher allowance
ssl_certificate /etc/ssl/yourapp.crt;
ssl_certificate_key /etc/ssl/yourapp.key;
}
Secure
# Nginx with modern TLS only — passes SSL Labs A+ rating
server {
listen 443 ssl http2;
ssl_protocols TLSv1.2 TLSv1.3; # Only modern protocols
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off; # TLS 1.3 ignores this
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off; # Disable for forward secrecy
ssl_certificate /etc/letsencrypt/live/yourapp.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourapp.com/privkey.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
Forcing HTTPS in Laravel AppServiceProvider
Vulnerable
// AppServiceProvider::boot() — no HTTPS enforcement
// Generated URLs may use HTTP, causing mixed content warnings
public function boot(): void
{
// Nothing — URLs use whatever scheme the request came in on
}
Secure
// AppServiceProvider::boot() — enforce HTTPS in production
public function boot(): void
{
if ($this->app->environment('production')) {
// Force all generated URLs to use https://
\Illuminate\Support\Facades\URL::forceScheme('https');
// Also trust your load balancer's X-Forwarded-Proto header
// (needed if SSL terminates at the load balancer/CDN)
\Illuminate\Support\Facades\Request::setTrustedProxies(
[\$_SERVER['REMOTE_ADDR']],
\Illuminate\Http\Request::HEADER_X_FORWARDED_FOR |
\Illuminate\Http\Request::HEADER_X_FORWARDED_PROTO
);
}
}
Real-World Example
An expired SSL certificate causes browsers to show a security warning, driving users away. Continuous monitoring can alert you days before expiration.
Why It Matters
SSL/TLS is the foundation of web security. Without it, all communication between your users and your Laravel application — passwords, session tokens, personal data — travels in plaintext that anyone on the network path can read. In 2026, there is no acceptable reason for a production web application to serve content over plain HTTP.
The practical concerns for Laravel developers are certificate management and TLS version support. SSL certificates expire, and an expired certificate causes browsers to display a hard warning that drives users away and breaks automated API integrations. Many teams have experienced an outage caused by a forgotten certificate renewal.
TLS 1.0 and 1.1 are deprecated and should be disabled in your web server configuration (Nginx ssl_protocols directive or Apache SSLProtocol directive). Only TLS 1.2 and 1.3 should be enabled. Many compliance frameworks (PCI DSS, HIPAA) explicitly require this.
For Laravel applications using Laravel Forge, Ploi, or similar server management tools, certificate renewal is often automated via Let's Encrypt. However, automation can fail silently — the cron job that triggers renewal may not run, the renewal may fail due to DNS propagation issues, or the web server may not reload after renewal. Monitoring certificate expiration dates provides an early warning system.
Common Misconceptions
Myth: Let's Encrypt certificates auto-renew, so I never need to worry about expiration.
Reality: Let's Encrypt auto-renewal requires a working cron job, HTTP access to your domain for the ACME challenge, and a web server reload after renewal. Any of these can fail silently. Monitor expiration dates regardless of automation.
Myth: Using HTTPS means our data is secure.
Reality: HTTPS encrypts data in transit but says nothing about security at rest (database encryption), access control, or application-level vulnerabilities. SSL/TLS is necessary but not sufficient for application security.
Myth: Our CDN handles SSL, so our origin server does not need it.
Reality: If the connection between your CDN and your origin server is not encrypted (using Cloudflare's "Flexible" SSL mode), data is transmitted in plaintext on the origin-side leg. Always use "Full (strict)" SSL mode with a valid certificate on your origin.
How to Detect This
Run `curl -vI https://yourdomain.com 2>&1 | grep -E "SSL|TLS|expire|subject|issuer"` to inspect certificate details including expiration date and issuer. Verify TLS 1.0 is disabled with `openssl s_client -connect yourdomain.com:443 -tls1 2>&1 | grep "alert handshake"` — you should see a handshake failure. Check your certificate expiration date with `echo | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates`. StackShield monitors SSL certificate expiration and TLS configuration on every scan, alerting you 30, 14, and 7 days before expiration and immediately if TLS configuration downgrades are detected.
How to Prevent This in Laravel
-
1
Configure Nginx with `ssl_protocols TLSv1.2 TLSv1.3;` and disable older versions — test with `openssl s_client -connect yourapp.com:443 -tls1` which should fail with a handshake alert.
-
2
Monitor certificate expiration with StackShield or a dedicated monitoring service — Let's Encrypt certificates expire every 90 days and auto-renewal cron jobs can fail silently.
-
3
Call `URL::forceScheme('https')` in `AppServiceProvider::boot()` for production and set `SESSION_SECURE_COOKIE=true` in `.env` to ensure generated URLs and session cookies use HTTPS.
-
4
Use a wildcard certificate (`*.yourapp.com`) or a multi-SAN certificate to cover all your subdomains — mismatched certificates on subdomains break API integrations silently.
-
5
Test your TLS configuration with SSL Labs (ssllabs.com/ssltest/) after any server or certificate changes — aim for an A+ rating and verify there are no weak cipher suites or protocol version issues.
Frequently Asked Questions
How do I force HTTPS in Laravel?
Call `URL::forceScheme('https')` in `AppServiceProvider::boot()` gated on `app()->environment('production')`. This ensures all URLs generated by Laravel (redirects, pagination, mail links) use `https://`. Additionally, configure your web server to redirect port 80 to 443, set `SESSION_SECURE_COOKIE=true` in `.env`, and add the HSTS header to prevent browsers from making initial HTTP requests.
How do I check which TLS versions my server supports?
Test each TLS version with openssl: `openssl s_client -connect yourapp.com:443 -tls1` should fail (TLS 1.0 disabled), `-tls1_1` should fail (TLS 1.1 disabled), `-tls1_2` and `-tls1_3` should succeed. For a comprehensive report, use SSL Labs at ssllabs.com/ssltest/ which grades your entire TLS configuration including cipher suites, protocol support, certificate chain, and known vulnerability checks.
Does Let's Encrypt auto-renew certificates automatically?
Let's Encrypt certificates require a working renewal mechanism — typically a certbot systemd timer or cron job that runs `certbot renew` twice daily and reloads Nginx after successful renewal. This can fail silently: the cron job may stop running, the ACME challenge may fail due to DNS changes or Nginx reconfiguration, or the post-renewal hook may not reload the web server. Monitor certificate expiration dates independently of your renewal mechanism, as renewal failures are a frequent cause of outages.
How do I fix mixed content warnings in Laravel?
Mixed content warnings appear when an HTTPS page loads resources over HTTP. In Laravel, the most common cause is generated URLs using HTTP because `URL::forceScheme('https')` is not set, or because the application is behind a load balancer that terminates SSL and the app sees HTTP internally. Fix by calling `URL::forceScheme('https')` in `AppServiceProvider::boot()` and configuring `TrustProxies` middleware to trust your load balancer's `X-Forwarded-Proto` header.
Related Terms
HTTP Strict Transport Security (HSTS)
A security header that tells browsers to only connect to your website over HTTPS. Once a browser receives the HSTS header, it will automatically convert all future HTTP requests to HTTPS for the specified duration, preventing protocol downgrade attacks and cookie hijacking.
Man-in-the-Middle Attack (MITM)
An attack where an adversary secretly intercepts and potentially modifies communication between two parties who believe they are communicating directly with each other. The attacker can read, inject, or alter data in transit.
Related Articles
SOC 2 Compliance for Laravel Applications: A Technical Implementation Guide
SOC 2 Type II compliance requires documented, auditable controls for security, availability, and confidentiality. This guide maps SOC 2 Trust Service Criteria to specific Laravel configurations and tells you exactly what evidence auditors will ask for.
How to Security Audit a Laravel Application: A Practical Guide
A step-by-step guide to auditing the security of a Laravel application. Covers dependency scanning, configuration review, external scanning, code review patterns, and how to prioritize findings.
Laravel Security Checklist 2026: 40 Checks Before Deploy
The 40 security checks we run on every Laravel app before it goes live. Most apps fail at least 5. Covers exposed .env files, debug mode, missing headers, CORS, session config, and dependency vulnerabilities.
Related Fix Guides
How to Fix Weak SSL/TLS Configuration in Laravel
Your SSL/TLS certificate is expired, misconfigured, or using weak protocols. Learn how to fix SSL issues for your Laravel app.
How to Fix an Exposed .git Directory
Your .git directory is publicly accessible, allowing attackers to download your entire source code and commit history. Fix it now.
How to Fix Subdomain Takeover Vulnerabilities
Dangling DNS records pointing to decommissioned services allow attackers to take over your subdomains. Learn how to find and fix them.
Monitor Your Laravel Application's Security
StackShield continuously checks your Laravel application from the outside, catching security issues before attackers find them.
Start Free Trial