Statamic Laravel Deployment

Statamic URLs Showing Server IP Instead of Your Domain? Here's the Fix

Interloper Digital 6 min read

You've deployed your Statamic site to production. Everything seems fine until you check your sitemap, or share a page, or look at any auto-generated URL—and it's showing your server's internal IP address instead of your actual domain.

http://192.168.1.50/blog/my-post

Instead of:

https://yourdomain.com/blog/my-post

If you're running behind RunCloud, Cloudflare, or any reverse proxy setup, this is almost certainly a trusted proxies issue.


A Note on Documentation

I want to be upfront: this issue is well-documented in Laravel's official documentation. The Configuring Trusted Proxies section covers exactly what's happening and how to fix it.

So why write this article?

Because when you're working primarily with Statamic—managing content, building templates, configuring blueprints—you're not necessarily thinking about Laravel middleware. Statamic does a great job of abstracting away most of the Laravel plumbing. You can build entire sites without touching bootstrap/app.php or thinking about HTTP request handling.

The problem surfaces when you deploy. Your local environment doesn't have a reverse proxy, so everything works. Then you push to production behind RunCloud or Cloudflare, and suddenly your sitemap has internal IPs, your Control Panel assets fail to load over HTTPS, and you're wondering what you broke.

This article exists to bridge that gap—to help Statamic developers who hit this issue recognise it as a Laravel configuration concern and point them to the right solution.


Why This Happens

When your application sits behind a reverse proxy (load balancer, CDN, managed hosting panel), it doesn't receive requests directly from the internet. The proxy forwards requests and adds special headers to communicate the original request details:

  • X-Forwarded-For — the client's real IP address
  • X-Forwarded-Host — the original domain requested
  • X-Forwarded-Proto — whether the original request was HTTP or HTTPS
  • X-Forwarded-Port — the original port

By default, Laravel ignores these headers for security reasons. As the fideloper/TrustedProxy package documentation explains: the application needs to explicitly trust specific proxies before it will read these forwarded headers.

Without that trust configured, Laravel sees the proxy's internal request—not the original one from the user. This cascades into Statamic:

  • Sitemap URLs generated by Statamic's SEO tools point to internal IPs
  • Asset URLs in the Control Panel use HTTP instead of HTTPS
  • Canonical URLs are wrong
  • The url() helper returns incorrect values throughout your templates

The Statamic GitHub discussions confirm this is a common issue for cloud deployments, with trusted proxies being the standard solution.


The Fix

Laravel's documentation provides the authoritative solution. In Laravel 11+ (which recent Statamic versions use), trusted proxies are configured in bootstrap/app.php:

use Illuminate\Http\Request;
->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustProxies(
        at: '*',
        headers: Request::HEADER_X_FORWARDED_FOR |
                 Request::HEADER_X_FORWARDED_HOST |
                 Request::HEADER_X_FORWARDED_PORT |
                 Request::HEADER_X_FORWARDED_PROTO
    );
})

The at: '*' setting tells Laravel to trust all proxies. This is appropriate for most managed hosting scenarios where you don't know (or can't easily determine) your proxy's IP address.

Source: Laravel 12.x Documentation — Configuring Trusted Proxies

For Tighter Security

If your infrastructure allows it, you can specify exact proxy IPs instead of trusting all:

->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustProxies(
        at: ['192.168.1.1', '10.0.0.0/8'],
        headers: Request::HEADER_X_FORWARDED_FOR |
                 Request::HEADER_X_FORWARDED_HOST |
                 Request::HEADER_X_FORWARDED_PORT |
                 Request::HEADER_X_FORWARDED_PROTO
    );
})

For Cloudflare specifically, they publish their IP ranges which you could hardcode. For most deployments behind managed services like RunCloud, DigitalOcean App Platform, or AWS load balancers, trusting all proxies is the practical choice.


After Deploying the Fix

Clear your caches to regenerate URLs with the correct domain:

php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan statamic:stache:clear

If you're using Statamic's static caching, regenerate those pages too.


Statamic-Specific Symptoms

While the underlying cause is a Laravel configuration issue, the symptoms often manifest through Statamic features:

Symptom What's Happening
Sitemap shows internal IPs Statamic's sitemap generator uses Laravel's URL helpers
CP assets fail to load (mixed content) Asset URLs generated with HTTP instead of HTTPS
Canonical meta tags are wrong SEO add-ons rely on correct URL generation
Glide image URLs use wrong domain Image manipulation URLs inherit the incorrect base
Form redirects break Post-submission redirects use internal addresses

If you're seeing any of these after deploying to a managed hosting environment, trusted proxies should be your first check.


Common Deployment Scenarios

RunCloud + DigitalOcean

RunCloud manages your server and sits between users and your PHP application. It handles SSL termination—meaning HTTPS connections terminate at RunCloud, and your app receives HTTP requests internally. Without trusted proxies, Laravel thinks every request is HTTP.

Cloudflare

Cloudflare proxies your traffic for CDN and security benefits. Your server receives requests from Cloudflare's IP range, not from users directly. The original client information is in the X-Forwarded-* headers.

DigitalOcean App Platform

Similar to other PaaS offerings—your app runs behind their load balancers. The Statamic community has confirmed this requires trusted proxy configuration.

Laravel Forge

Even with Forge's streamlined deployment, if you're using a load balancer or CDN in front of your server, you'll need trusted proxies configured. Forge deploys to bare servers, so if you're accessing them directly (no proxy), you won't see this issue.


Verifying the Fix

After deploying and clearing caches:

  1. Check your sitemap. Visit /sitemap.xml and verify URLs use your domain with HTTPS.

  2. Test the url() helper in Tinker:

    url('/test');
    // Should return: https://yourdomain.com/test
    
  3. Check generated asset URLs. View page source and verify CSS/JS assets use the correct protocol and domain.

  4. Test HTTPS detection:

    request()->secure();
    // Should return: true
    

The .env Side of This

Make sure your .env file has the correct APP_URL:

APP_URL=https://yourdomain.com

This is used as a fallback for artisan commands and queued jobs that don't have a request context. If this is wrong, some URL generation will still be incorrect even with trusted proxies configured.


Further Reading


Summary

This isn't a Statamic bug or a gap in Statamic's documentation—it's standard Laravel behaviour that becomes relevant when you deploy behind a reverse proxy. The fix is well-documented in Laravel's official docs.

The reason it catches Statamic developers off guard is that you can work with Statamic extensively without ever touching Laravel's HTTP layer. Then you deploy to production, hit this issue, and don't immediately recognise it as a Laravel middleware concern.

If your Statamic site's URLs are showing internal IPs or HTTP instead of HTTPS after deployment: check trusted proxies first, and consult the Laravel documentation for the implementation details.

Deployment Giving You Headaches?

Book a free Discovery Audit and get expert guidance on your deployment challenges.

Book a Discovery Audit