Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Controlling the prod Environment

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $10.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Let's see what our app looks like if we change to the prod environment. To do that, open the .env file and change APP_ENV to prod:

22 lines .env
... lines 1 - 15
###> symfony/framework-bundle ###
... lines 18 - 22

Clearing Cache in the prod Environment

Cool! Now, find your browser, refresh and... it works! Well, actually, we got lucky. Behind the scenes, when we load a page, Symfony caches configuration, templates and other things for performance. In the dev environment, if we update a config file, Symfony automatically rebuilds the cache. So it's not something we even need to think about.

But in the prod environment - which is primed for performance - Symfony does not automatically rebuild your cache files. For example, if we added a new route to our app and then went to that URL in the prod environment, it would give us a page not found error! Why? Because our app would be using outdated routing cache.

That's why, whenever you change to the prod environment, you need to find your terminal and run a special command:

php bin/console cache:clear

This clears the cache so that, on our next reload, new cache will be built. The cache is stored in a var/cache/prod directory. Oh, and notice that bin/console is smart enough to know that we're in the prod environment.

In practice, I rarely switch to the prod environment on my local computer. The most common time I run cache:clear is when I'm deploying.

Now our app definitely works. And notice: no web debug toolbar!

Let's see Symfony's automatic caching system in action. Open up templates/question/show.html.twig and... let's make some small change - like Question::

... lines 1 - 4
{% block body %}
<div class="container">
<div class="row">
<div class="col-12">
<h2 class="my-4">Question:</h2>
... lines 10 - 26
... lines 29 - 56
{% endblock %}

This time, when we refresh, the change is not there. That's because Symfony caches Twig templates. Now find your terminal, run:

php bin/console cache:clear

And come back to refresh. There's the change!

Different Cache Adapter in prod

Now that we understand environments, I have a challenge for us! At the top of the page, we're still dumping the cache service inside our controller. The class is ApcuAdapter because that's what we configured inside of config/packages/cache.yaml:

... lines 3 - 13
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
app: cache.adapter.apcu
... lines 16 - 20

APCu is great. But maybe for simplicity, because it requires you to have a PHP extension installed, we want to use the filesystem adapter in the dev environment and APCu only for prod. How could we do that?

Let's think about it: we know how to override configuration in a specific environment... so we could override just this one config key in the dev environment.

To do that, in the dev/ directory, create a new file. It technically doesn't matter what it's called, but because we value our sanity, call it cache.yaml. Inside, say framework:, cache:, app: and the name of the original default value for this: cache.adapter.filesystem:

app: cache.adapter.filesystem

That's... all we need! Let's see if it works! Because we're still in the prod environment, find your terminal and clear the cache:

php bin/console cache:clear

When it finishes, go refresh the page. Good: in prod it's still using ApcuAdapter. Now go find the .env file at the root of the project... change APP_ENV back to dev:

22 lines .env
... lines 1 - 15
###> symfony/framework-bundle ###
... lines 18 - 20

And refresh the page.

Because the web debug toolbar is back, our dump is hiding inside its target icon. Let's see... yes! It's FilesystemAdapter!

Ok team: we've mastered environments and configuring services that are coming from bundles. So let's take things up to the next level: let's create our own service objects! That's next.

Leave a comment!

Login or Register to join the conversation
Joao P. Avatar
Joao P. Avatar Joao P. | posted 3 years ago

Funny, when I changed to the "dev" environment, the class remained "apcu", even though I've hit the reload. I had to clear the cache in the "dev" environment to see the change. I seems not everything in the cache is rebuilt in symfony's development server.

1 Reply

Hey Joao,

It should be rebuilt, but sometimes, especially when you use some virtualization tools like Docker, Vagrant, etc. it may not see the changes. But yeah, in any weird case the first thing to do after page refresh would be clearing the cache :)

I'm glad that cache:clear helped! And thanks for sharing it with others.


1 Reply
MattWelander Avatar
MattWelander Avatar MattWelander | posted 1 year ago

Hi, hope this isn't off topic. In my prod environment I wonder if my htaccess is setup correctly... because the "public" is normally not visible, but if I type in "mysite.com/public/" that works just as well as just "mysite.com/" ... it might be a security issue? I thought, if my htaccess is properly configured, the "public/" should always be hidden, yea?

The htaccess file in the root folder looks like this:

RewriteEngine On

RewriteBase /

RewriteCond %{REQUEST_URI} !^/public/
RewriteRule ^(.*)$ public/$1

The one in the public/ folder looks like this (I think it's identical to the symfony repo):

# Use the front controller as index file. It serves as a fallback solution when
# every other rewrite/redirect fails (e.g. in an aliased environment without
# mod_rewrite). Additionally, this reduces the matching process for the
# start page (path "/") because otherwise Apache will apply the rewriting rules
# to each configured DirectoryIndex file (e.g. index.php, index.html, index.pl).
DirectoryIndex index.php

# By default, Apache does not evaluate symbolic links if you did not enable this
# feature in your server configuration. Uncomment the following line if you
# install assets as symlinks or if you experience problems related to symlinks
# when compiling LESS/Sass/CoffeScript assets.
# Options +FollowSymlinks

# Disabling MultiViews prevents unwanted negotiation, e.g. "/index" should not resolve
# to the front controller "/index.php" but be rewritten to "/index.php/index".
<IfModule mod_negotiation.c>
    Options -MultiViews

<IfModule mod_rewrite.c>
    RewriteEngine On

    # Determine the RewriteBase automatically and set it as environment variable.
    # If you are using Apache aliases to do mass virtual hosting or installed the
    # project in a subdirectory, the base path will be prepended to allow proper
    # resolution of the index.php file and to redirect to the correct URI. It will
    # work in environments without path prefix as well, providing a safe, one-size
    # fits all solution. But as you do not need it in this case, you can comment
    # the following 2 lines to eliminate the overhead.
    RewriteCond %{REQUEST_URI}::$0 ^(/.+)/(.*)::\2$
    RewriteRule .* - [E=BASE:%1]

    # Sets the HTTP_AUTHORIZATION header removed by Apache
    RewriteCond %{HTTP:Authorization} .+
    RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]

    # Redirect to URI without front controller to prevent duplicate content
    # (with and without `/index.php`). Only do this redirect on the initial
    # rewrite by Apache and not on subsequent cycles. Otherwise we would get an
    # endless redirect loop (request -> rewrite to front controller ->
    # redirect -> request -> ...).
    # So in case you get a "too many redirects" error or you always get redirected
    # to the start page because your Apache does not expose the REDIRECT_STATUS
    # environment variable, you have 2 choices:
    # - disable this feature by commenting the following 2 lines or
    # - use Apache >= 2.3.9 and replace all L flags by END flags and remove the
    #   following RewriteCond (best solution)
    RewriteCond %{ENV:REDIRECT_STATUS} =""
    RewriteRule ^index\.php(?:/(.*)|$) %{ENV:BASE}/$1 [R=301,L]

    # If the requested filename exists, simply serve it.
    # We only want to let Apache serve files and not directories.
    # Rewrite all other queries to the front controller.
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ %{ENV:BASE}/index.php [L]

<IfModule !mod_rewrite.c>
    <IfModule mod_alias.c>
        # When mod_rewrite is not available, we instruct a temporary redirect of
        # the start page to the front controller explicitly so that the website
        # and the generated links can still be used.
        RedirectMatch 307 ^/$ /index.php/
        # RedirectTemp cannot be used instead
Jesse-Rushlow Avatar
Jesse-Rushlow Avatar Jesse-Rushlow | SFCASTS | MattWelander | posted 1 year ago

Howdy Matt! The public directory should be the base or DocumentRoot (Apache) directory in your configuration. Anything in public is meant to be exposed to the world. E.g. when you goto https://some-site.com that should point directly to /some/path/public

They most important thing is that you do not want to be able to access say https:://some-site.com/.env or https://some-site.com/src/Entity/User.php

https://symfony.com/doc/current/setup/web_server_configuration.html has a few more examples of setting up Symfony w/ Apache, Nginx, etc...

I hope this helps!

Thomas-34 Avatar
Thomas-34 Avatar Thomas-34 | posted 1 year ago

I use an ajax request with jquery to get my data from Data Base and when,
I switch from APP_ENV=dev to APP_ENV=prod, I get an undefined response.dataClubInfo from my controller JsonResponse.

I have in particular this error:

Uncaught TypeError: Cannot read properties of undefined (reading 'nomClub')
at Object.success (infoClub.js:31)
at fire (jquery.js:3500)
at Object.fireWith [as resolveWith] (jquery.js:3630)
at done (jquery.js:9796)
at XMLHttpRequest.<anonymous> (jquery.js:10057)

What may be the issue ?


Hey Thomas-34

I believe you forgot to clear production's cache. When in production, the cache does not get cleared automatically, you need to do it yourself. Give it a try and let me know if the problem persists


Thomas-34 Avatar

Thank you very much for your quick answer diego but the problem don't seems to come from the cache. I've runned my project from a brand new clone coming from my git repo, and even done a "bin/console cache:clear" and still have an undefined variable when switching from dev to prod.


That's interesting. The error only happens when you switch to production, right? If that's the case, I'd check the followign

  • Environment variables. It's possible you're using a different value which does not work
  • Installed bundles. You may be working with a bundle that's not enabled for the PROD environment
  • Services definition & config. Double-check that your services definition and configuration are correct for the PROD env
  • Rebuild your assets using yarn build
Thomas-34 Avatar

Thank you, I dindn't think about my dependecies.

It didn't work but I am on a track:

When using postman, the request to my controller give me 2 things:

<script> Sfdump = window.Sfdump || (function (doc) ...... </script>
=> it' what I can read when doing console.log(response);
{"dataClubInfo":{"nomClub":"......}} with my preciouse data !
=> it's the response I get from my controller return new JsonResponse(["dataClubInfo"=>$retourClubTab]);

To resume on dev I have an objet with my data ( {dataClubInfo: {…}} ) and in prod I get no data but :<script> Sfdump = window.Sfdump )

I hope you'll be able to help me now.


Thomas-34 Avatar

The answer is beacause I didn't comment all my dump() before return new JsonResponse in my controller.


Aha! I knew something was off there. Cheers!

Mr B. Avatar

I seem to have lost the certificates, again, after switching to production, but this time I cannot get the localhost to load - it's not secure - Your connection is not private


Hey Tom,

Do you have problems with with certs on the localhost? You can reinstall them. Try to stop your web server, do "symfony server:ca:uninstall", and then "symfony server:ca:install". Look for the output for any errors/warnings. Of everything is OK - then start your server again.

I hope this helps!


Default user avatar
Default user avatar Tao Man | posted 2 years ago | edited

When I switch to the prod environment, I get an "500 Internal Server Error".

symfony server:log

reveals that the ExceptionListener class was not found.
[PHP-CGI ] {"message":"Uncaught Error: Class \"Symfony\\WebpackEncoreBundle\\EventListener\\ExceptionListener\" not fistener\" not found","code":0,"file":"C:\\Users\\tbrem\\source\\repos\\cauldron_overflow\\var\\cache\\prod\\ContainerX13osoQ\\getWebpackEncore_ExceptionListenerService.php:20"}},"level":500,"level_name":"CRITICAL","channel":"php","datetime":"2021-05-14T12:46:49.632095+02:00","extra":{}}

What could be the issue?


Hey Tao Man

Did you cleared the production's cache? When on production, you're now in charge or clearing the cache everytime you make a change. Give it a try and let me know if you keep having problems


Badii Avatar

Hi i did try it but it didn't work

1 Reply

Hey Badii

Could you tell me more about your problem? and sorry for my late reply

1 Reply
Ivan-P Avatar

Thanks for the cool courses in here!!

Regarding environments I wanted to ask, what role the use of app.php or app_dev.php plays in here and how do we use different .env files (env.{env}....).

(Sorry about the open question)


Hey Abel,

Thank you for your interest in SymfonyCasts tutorials and kind words about courses!

Hm, good question! If you're a new in Symfony - you probably know only one front controller called "public/index.php" in Symfony projects where the environment of the application is controlled via env vars: real env vars that you can set in your console, or "fake" env vars taken from .env files. Generally speaking, if you need to change the env - you just need to set the correct value to APP_ENV env var.

But before .env files there were parameters.yml files in Symfony projects, and projects had more than 1 front controller. Usually, as many as number of environments existent in your app - they helped to load the website in the specific environment easily. E.g. app.php loaded the website in prod, where app_dev.php as you can guess already loaded the website in dev mode. If you look at those files - you will see that environment is hardcoded in those files.

So, in short, before Symfony started using env vars developers used different front controllers to load the website in different environments: prod, dev, test, etc. without changing anything in the code. But now, since you can control the env with env vars very easily even on the go, there's no reason to keep more than one front controller, that why only the index.php file is kept that works with env vars now :)

I hope this helps! :)


Georg H. Avatar
Georg H. Avatar Georg H. | posted 3 years ago

Hi there,
is it possible to launch commands like cache:clear from within the app. That would make deployment a bit easier for me.


Hey @Georg

It's a little tricky, you can do it via symfony/process component which allows to execute shell commands, so you just need run you command programmatically, but it can be pretty unstable. It's totally depends on your workflow, probably if you explain your deploy process and how and when you want to run cache:clear command I'll give you more correct advise =)


Georg H. Avatar

Hey Vladimir
I got an account to transfer the symfony files on the production server. The cache/prod directory and the files therein belong to a different user. The hosting service provides an extra web-tool to let me delete cache/prod. Using symfony/process I could delete the cache in my application. However, I understand that deleting the cache with symfony/process from the web application, that is currently using it, might not be a good idea. It's good to know that symfony/process exists anyway!


Hey @Georg!

In case it helps, you can, in practice, "get away with" just deleting the var/cache/prod directory manually. The cache:clear command basically does that, then pre-warms the cache. But if you delete the directory, then the first request after would re-build the cache. It IS possible also to run "commands" (even without the Process component) from inside Symfony... but yea... this one is iffy :). If you deploy some new code, your app might not even run until after your clear the cache (and so, you might be even be able to run your app to run that command) 🙃.

Anyways, let us know if this helps. A lot of the "best ways" to do things depend on how you deploy. If you don't have ssh access to run commands directly, it does make things a bit tricker :).


Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial also works great for Symfony 6!

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": "^7.3.0 || ^8.0.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
        "sensio/framework-extra-bundle": "^6.0", // v6.2.1
        "sentry/sentry-symfony": "^4.0", // 4.0.3
        "symfony/asset": "5.0.*", // v5.0.11
        "symfony/console": "5.0.*", // v5.0.11
        "symfony/debug-bundle": "5.0.*", // v5.0.11
        "symfony/dotenv": "5.0.*", // v5.0.11
        "symfony/flex": "^1.3.1", // v1.17.5
        "symfony/framework-bundle": "5.0.*", // v5.0.11
        "symfony/monolog-bundle": "^3.0", // v3.6.0
        "symfony/profiler-pack": "*", // v1.0.5
        "symfony/routing": "5.1.*", // v5.1.11
        "symfony/twig-pack": "^1.0", // v1.0.1
        "symfony/var-dumper": "5.0.*", // v5.0.11
        "symfony/webpack-encore-bundle": "^1.7", // v1.8.0
        "symfony/yaml": "5.0.*" // v5.0.11
    "require-dev": {
        "symfony/maker-bundle": "^1.15", // v1.23.0
        "symfony/profiler-pack": "^1.0" // v1.0.5