Skip to content

Latest commit

 

History

History
148 lines (113 loc) · 6.08 KB

File metadata and controls

148 lines (113 loc) · 6.08 KB

Server Configuration

Overview

Pollora uses a Bedrock-style directory layout where WordPress core, plugins, themes, and uploads all live under the public/ document root. This differs from a standard WordPress installation and requires specific web server configuration to prevent exposing internal directories.

Document Root

Your web server's document root must point to the public/ directory:

your-project/
├── app/
├── bootstrap/
├── config/
├── public/          ← document root
│   ├── index.php    ← Laravel front controller
│   ├── wp-config.php
│   ├── cms/         ← WordPress core
│   └── content/     ← wp-content (plugins, themes, uploads)
├── resources/
└── vendor/

Directory Browsing Protection

The Problem

Because content/ (WordPress's wp-content) is under the public document root, directories like /content/plugins/, /content/themes/, and /content/uploads/ are web-accessible. By default, some of these directories contain index.php files with only a comment (// Silence is golden.), which causes the web server to return a blank 200 response — confirming the directory exists and leaking structural information.

The fix is to ensure your web server never resolves DirectoryIndex for these paths, and instead routes all non-file requests through the Laravel front controller.

Apache Configuration

Since v13.34.1, the .htaccess shipped with the skeleton (public/.htaccess) sends these requests to the front controller, which answers with your site's own 404 page. A project created before that can add the same rule, before the "Redirect Trailing Slashes" block:

    # WordPress Content Directories Are Not Pages...
    RewriteCond %{REQUEST_FILENAME} -d [OR]
    RewriteCond %{REQUEST_FILENAME} /index\.php$
    RewriteRule ^(cms/wp-content|content)(/|$) index.php [L]

This ensures that:

  • Directories under /cms/wp-content/ and /content/ — and the empty index.php WordPress ships in some of them — answer your site's 404 instead of a blank 200 or a 403
  • Files in them (plugin CSS and JS, uploads, fonts) are served directly by Apache, as before
  • The rest of the site is untouched: /cms/wp-admin/ and the front controller keep their index.php

Options -Indexes, also in the shipped .htaccess, keeps directory listing off everywhere else.

Nginx Configuration

For Nginx, the key is to remove the $uri/ directive from try_files, which prevents Nginx from resolving DirectoryIndex for arbitrary directories:

server {
    listen 80;
    server_name example.com;
    root /var/www/your-project/public;
    index index.php;

    # Serve static files directly, everything else goes to the framework.
    # Intentionally omit $uri/ to prevent DirectoryIndex resolution
    # for paths like /content/plugins/ or /content/themes/.
    location / {
        try_files $uri /index.php?$query_string;
    }

    # wp-admin needs DirectoryIndex resolution for its own index.php.
    location ^~ /cms/wp-admin {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # PHP handling
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # Deny access to hidden files
    location ~ /\. {
        deny all;
    }
}

Note
The critical difference from a standard Laravel Nginx config is try_files $uri /index.php?$query_string; (without $uri/). The $uri/ directive tells Nginx to look for an index.php inside the requested directory — which is exactly what causes the blank 200 responses.

Reverse Proxy & HTTPS

How Pollora Handles HTTPS

When deployed behind a reverse proxy (Clever Cloud, Heroku, AWS ALB, Cloudflare, etc.), the backend server often receives requests over HTTP on an internal port (e.g., 127.0.0.1:8080), while the public-facing URL uses HTTPS.

Pollora derives the HTTPS state from the APP_URL environment variable — not from request headers. When APP_URL starts with https://, Pollora automatically:

  1. Forces Laravel's URL generator to use HTTPS (URL::forceScheme('https'))
  2. Sets $_SERVER['HTTPS'] = 'on' so WordPress canonical redirects work correctly
  3. Computes WP_HOME, WP_SITEURL, and WP_CONTENT_URL from APP_URL

This means you do not need to manually configure X-Forwarded-Proto header mapping. Just ensure APP_URL is set correctly in your .env:

APP_URL=https://your-domain.com

TrustProxies Middleware

While Pollora handles HTTPS and WordPress URLs via APP_URL, you may still want to configure Laravel's TrustProxies middleware for other features that rely on the original client request (client IP, rate limiting, etc.).

In bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
    $middleware->trustProxies(
        at: '*',  // or specific proxy IPs
        headers: Request::HEADER_X_FORWARDED_FOR |
                 Request::HEADER_X_FORWARDED_HOST |
                 Request::HEADER_X_FORWARDED_PORT |
                 Request::HEADER_X_FORWARDED_PROTO |
                 Request::HEADER_X_FORWARDED_AWS_ELB,
    );
})

Warning
Using at: '*' trusts all proxies. In production, restrict this to your reverse proxy's IP range when possible.