Configuring a Caddy webserver for Symfony

1. Caddy version (caddy version):

n/a

2. How I run Caddy:

a. System environment:

n/a

b. Command:

n/a

c. Service/unit/compose file:

n/a

d. My complete Caddyfile or JSON config:

n/a

3. The problem I’m having:

I thought I’d have a crack at providing a Caddy example for Symfony. I chose Symfony because an NginX example is provided on the Symfony site and I’m curious to see how that converts to Caddy. It seems like it would be a more straightforward conversion than translating from Apache to Caddy.

The challenge here is that I don’t use Symfony or NginX so I’m totally reliant on forum members and Caddy core maintainers to help me with the conversion and testing. If any forum members use Symfony and can test the end product of this exercise, that would be super helpful.

More info in the next post.

4. Error messages and/or full log output:

n/a

5. What I already tried:

n/a

6. Links to relevant resources:

  1. Terraforming the application landscape to help spread the Caddy message - #3 by matt
  2. Configuring a Web Server (Symfony Docs)

This is the section on the Symfony site of interest:

Nginx

The minimum configuration to get your application running under Nginx is:

server {
    server_name domain.tld www.domain.tld;
    root /var/www/project/public;

    location / {
        # try to serve file directly, fallback to index.php
        try_files $uri /index.php$is_args$args;
    }

    # optionally disable falling back to PHP script for the asset directories;
    # nginx will return a 404 error when files are not found instead of passing the
    # request to Symfony (improves performance but Symfony's 404 page is not displayed)
    # location /bundles {
    #     try_files $uri =404;
    # }

    location ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;

        # optionally set the value of the environment variables used in the application
        # fastcgi_param APP_ENV prod;
        # fastcgi_param APP_SECRET <app-secret-id>;
        # fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name";

        # When you are using symlinks to link the document root to the
        # current version of your application, you should pass the real
        # application path instead of the path to the symlink to PHP
        # FPM.
        # Otherwise, PHP's OPcache may not properly detect changes to
        # your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
        # for more information).
        # Caveat: When PHP-FPM is hosted on a different machine from nginx
        #         $realpath_root may not resolve as you expect! In this case try using
        #         $document_root instead.
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        # Prevents URIs that include the front controller. This will 404:
        # http://domain.tld/index.php/some-path
        # Remove the internal directive to allow URIs like this
        internal;
    }

    # return 404 for all other php files not matching the front controller
    # this prevents access to other php files you don't want to be accessible.
    location ~ \.php$ {
        return 404;
    }

    error_log /var/log/nginx/project_error.log;
    access_log /var/log/nginx/project_access.log;
}

If you use NGINX Unit, check out the official article about How to run Symfony applications using NGINX Unit.

NOTE

Depending on your PHP-FPM config, the fastcgi_pass can also be fastcgi_pass 127.0.0.1:9000.

TIP

This executes only index.php in the public directory. All other files ending in “.php” will be denied.

If you have other PHP files in your public directory that need to be executed, be sure to include them in the location block above.

CAUTION

After you deploy to production, make sure that you cannot access the index.php script (i.e. http://example.com/index.php).

For advanced Nginx configuration options, read the official Nginx documentation.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.

Caddy code considerations in the next post.

A config for Symfony would, by default, look just the same as one for WordPress that you wrote up the other day, except that root will have /public appended (like the nginx config above).

Symfony uses index.php as the routing entrypoint, so the php_fastcgi directive will do the job just fine.

Symfony’s defaults are better in that they recommend a public/ directory as the webroot, where the rest of the code sources are siblings of that directory. That means that actual source files cannot be directly accessed, (unlike WordPress, which does not use a subdirectory as the webroot, which is a modern best-practices no-no).

1 Like

Caddy equivalent code

domain.tld www.domain.tld { 
    # Set this path to your site's directory:
    root * /var/www/project/public

    # Enable Gzip compression:
    encode gzip

    # Serve PHP files through php-fpm:
    php_fastcgi unix//run/php/php7.4-fpm.sock

    # Enable the static file server:
    file_server

    # Enable HTTP request logging
    log {
        output file /var/log/caddy/access.log
    }
}

:writing_hand: Depending on your PHP-FPM config, the php_fastcgi can also be php_fastcgi 127.0.0.1:9000.

Suggestions for improvement to code or comments? What about the placement of the log directive?

The NginX code has error logging. Where does that happen for Caddy? In the Caddy process log? If so, do I need to include a global section in the Caddy code and reference the process log?

Interesting!

Bump post #4

LGTM.

Pretty sure php-fpm should do the logging. Some reading:

1 Like

Based on this article Configuring the Nginx Error Log and Access Log and in particular the section What is an Nginx error log?, I wonder if I should enable the Caddy process log?

{
    # Enable process logging
    log {
        output file /var/log/caddy/caddy.log
    }
} 
domain.tld www.domain.tld { 
    # Set this path to your site's directory:
    root * /var/www/project/public

    # Enable Gzip compression:
    encode gzip

    # Serve PHP files through php-fpm:
    php_fastcgi unix//run/php/php7.4-fpm.sock

    # Enable the static file server:
    file_server

    # Enable HTTP request logging
    log {
        output file /var/log/caddy/access.log
    }
}

From post #2, NginX has been configured to behave in a certain way and the comments reflect this. Caddy default behaviour is likely to be different. Please comment on Caddy behaviour for each of these NginX comments:

  1. Caddy unit?
  1. Caddy’s handling of PHP files
  1. Not sure I understand this one…
  1. Licensing?

Not necessary if running as a systemd service, because the journal will have your logs already. See Keep Caddy Running — Caddy Documentation. Similar deal with Docker with docker logs <caddy container ID>

NGINX Unit is a totally different app than NGINX proper. It’s a server without config files, configured entirely via a JSON config API. (Sound familiar? :wink: API — Caddy Documentation)

I don’t think that’s worth worrying about. Caddy will execute any .php files in public/, defaulting to index.php, but I see no issue with that. Just makes it easier to add some one-off scripts for temporary testing or whatever. Devs rarely do put any other PHP scripts in public though.

They mean to prevent URLs like /index.php/foo/bar which is technically the same as /foo/bar due to the way index.php is used as an entrypoint, using the remainder as the PATH_INFO variable passed to PHP. (Try it with your WordPress app for paths to some blog article for example, put /index.php in front of the path)

Preventing both from working would avoid duplicate possible URLs (for SEO or some possible edge-case security bugs, I guess). Caddy’s default doesn’t prevent this.

It should be trivial to prevent with a path_regexp + redir to force the browser to request without a /index.php, but IMO that’s not worth worrying about, it isn’t worth the added config.

:man_shrugging:

That’s just saying those docs are CC license, which I guess only you need to care about, if you do at all, since you’re authoring these changes.

1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.