


How to configure PHP environment with Docker to support SSL PHP container to enable HTTPS access method
Jul 25, 2025 pm 05:48 PMTo make PHP applications support HTTPS in Docker, the core is to configure the SSL certificate and key into Nginx or Apache containers and ensure that they work in conjunction with the PHP-FPM containers. 1. Create a self-signed certificate for use in the development environment; 2. Write a Dockerfile for PHP-FPM and Nginx; 3. Configure Nginx to enable HTTPS and forward PHP requests to PHP-FPM; 4. Use docker-compose to orchestrate the service and mount the certificate and code directory; 5. Modify the local hosts file to resolve the domain name to 127.0.0.1. If HTTPS is inaccessible or certificate error occurs, common reasons include: certificate path error, port not exposed or occupied, Nginx configuration syntax error, firewall restrictions, self-signed certificate not trusted, mixed content issues, or DNS resolution errors. The key points of quickly building HTTPS in the development environment are: use openssl to generate certificates, unified certs directory, docker-compose mount certificates, Nginx configuration correct path, and setting hosts files. The production environment should use Let's Encrypt and Certbot to automatically manage certificates, obtain certificates through independent Certbot containers in conjunction with HTTP or DNS challenges, and configure an automatic renewal mechanism to ensure that the certificates are valid for a long time; at the same time, pay attention to security measures such as permission control, backup, and email notifications.
To put it bluntly, let your PHP application run on HTTPS in Docker, it is necessary to properly install your SSL certificate and key in a container on a web server (such as Nginx or Apache), then configure it to listen for port 443, and use these certificates to encrypt communication. At the same time, ensure that the web server and PHP-FPM containers can work seamlessly to allow your PHP code to be executed in a secure environment.

Solution
To get this done, we usually use docker-compose
to orchestrate the two core services of Nginx (or Apache) and PHP-FPM. The core idea is: Nginx is responsible for receiving HTTPS requests, processing SSL handshakes, and then forwarding PHP-related requests to the PHP-FPM container for processing.
We first prepare a self-signed SSL certificate, which is particularly convenient in the development environment and saves the hassle of applying for a formal certificate. Create a certs
folder in the root directory of your project and execute:

mkdir -p certs openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout certs/nginx.key -out certs/nginx.crt -subj "/CN=yourdomain.local"
Here yourdomain.local
can be any domain name pointing to 127.0.0.1
in your local hosts file, such as app.local
.
Next, we need a Dockerfile
to build our PHP-FPM service, and an Nginx configuration file.

1. php/Dockerfile
(assuming it is placed in the php
folder in the root directory of the project):
FROM php:8.2-fpm-alpine # Install commonly used PHP extensions and add RUN docker-php-ext-install pdo_mysql opcache according to your project requirements WORKDIR /var/www/html
2. nginx/Dockerfile
(assuming it is placed in the nginx
folder in the root directory of the project):
FROM nginx:alpine # Copy custom Nginx configuration COPY nginx.conf /etc/nginx/conf.d/default.conf # Copy the certificate file, here assuming the certificate is in the certs folder of the project root directory# mount through volume in docker-compose.yml is more flexible, here is just an example# COPY ../certs/nginx.crt /etc/nginx/certs/nginx.crt # COPY ../certs/nginx.key /etc/nginx/certs/nginx.key WORKDIR /var/www/html
3. nginx/nginx.conf
(Nginx HTTPS configuration):
server { listen 80; listen [::]:80; server_name yourdomain.local; # Replace with your domain name return 301 https://$host$request_uri; # Force HTTP to jump to HTTPS } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name yourdomain.local; # Replace with your domain name# SSL certificate path, here it will be mounted through docker-compose volumes ssl_certificate /etc/nginx/certs/nginx.crt; ssl_certificate_key /etc/nginx/certs/nginx.key; # Recommended SSL configuration ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; add_header X-XSS-Protection "1; mode=block"; root /var/www/html/public; # Your application portal directory, such as Laravel's public index index.php index.html index.htm; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass php:9000; # php is the docker-compose service name, 9000 is the default port of PHP-FPM fastcgi_index index.php; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } # Hide Nginx version information server_tokens off; }
4. docker-compose.yml
(orchestration service):
version: '3.8' services: nginx: build: context: ./nginx # Nginx's Dockerfile path ports: - "80:80" - "443:443" Volumes: - ./src:/var/www/html # Mount your project code - ./certs:/etc/nginx/certs # Mount SSL certificate and key - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro # Make sure that the Nginx configuration is loaded correctly depends_on: - php restart: unless-stopped php: build: context: ./php # PHP's Dockerfile path volumes: - ./src:/var/www/html # Mount your project code restart: unless-stopped # If necessary, the port of PHP-FPM can be exposed, but usually Nginx can access internally # ports: # - "9000:9000"
5. src/public/index.php
(a simple test file):
<?php echo "Hello from HTTPS PHP on Docker!"; phpinfo(); ?>
Finally, run docker-compose up -d --build
in the root directory of your project, and then modify your local hosts
file (Windows in C:\Windows\System32\drivers\etc\hosts
, macOS/Linux in /etc/hosts
), and add a line:
127.0.0.1 yourdomain.local
Now, visit https://yourdomain.local
, you should be able to see the output of PHP and the browser will prompt the certificate to be distrusted (because it is self-signed), but the connection is encrypted.
Why is my HTTPS still inaccessible after configuration, or I get a certificate error?
This question is good. Every time I do SSL, I always encounter some strange pitfalls. I personally think that the most common ones are just a few:
- Certificate path or name is wrong: This is the most likely mistake for beginners to make. Do the paths filled in
ssl_certificate
andssl_certificate_key
really point to the correct file inside the container? You may be correct in the host, but after mounting it in the container, the path changes. Check thevolumes
configuration indocker-compose.yml
to make sure that the certificate file is indeed mounted to the path specified innginx.conf
inside the Nginx container. For example, in my example above, the certificate path inside the Nginx container is/etc/nginx/certs/nginx.crt
, and the host is./certs/nginx.crt
. If the path is written incorrectly, Nginx will report an error when starting, or SSL will be directly refused to be loaded. - The port is not exposed or occupied: Make sure that
ports
part ofnginx
service indocker-compose.yml
has"443:443"
, which means that the host's port 443 will be mapped to the Nginx container's port 443. If the host's port 443 has been occupied by other services (such as IIS, Apache, or other Nginx instances), Docker cannot be bound and the container may fail to start. At this time, you can try mapping to other ports, such as"8443:443"
, and then visithttps://yourdomain.local:8443
. - Nginx configuration syntax error: Don't underestimate this. The absence of a bracket or a semicolon can make Nginx strike directly. You can go inside the Nginx container and run
nginx -t
to check the syntax of the configuration file.docker-compose exec nginx nginx -t
is a good habit. - Firewall blocks: Does the security group rules of the host or cloud server block port 443? This is particularly common when deploying to a production environment, and local development environments are relatively rare, but not impossible.
- "Expected" error for self-signed certificates: If you are using a self-signed certificate, the browser will definitely prompt "unsafe connection" or "privacy error". This is actually normal because the browser does not trust you as a private issuer. This is not a configuration error, but a security mechanism. You need to manually add exceptions in your browser or trust this certificate.
- Mixed Content Warning: Your page is loaded via HTTPS, but the page refers to certain HTTP resources (such as images, CSS, JS files). The browser will consider this to be unsafe and may prevent these HTTP resources from loading, or give a warning in the console. The solution is to make sure all resources are loaded over HTTPS.
- DNS resolution question: Did the domain name you visited correctly resolve to the IP address of the Docker host? Especially if you configure
yourdomain.local
in thehosts
file, but the browser may cache the old resolution, or you access the domain name you configured at all.
When encountering problems, don't panic. First look at the logs of the Docker container ( docker-compose logs nginx
). You can usually find clues.
How to quickly build a PHP container that supports HTTPS in a development environment?
In the development environment, our core demands are "fast" and "simple", and we don't have to worry too much about the authority of the certificate. In the above solution, a very fast method has been given, the core is to self-signed certificates .
To put it bluntly, you don’t need to apply for Let’s Encrypt, and you don’t need to spend money on business certificates. Just use the openssl
command to generate a pair of keys and certificate files yourself. I personally usually do this:
- Unified
certs
directory: I will put acerts
folder outside all my Docker projects, or in the root directory of each project. - Simple
openssl
command:mkdir -p certs openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout certs/dev.key -out certs/dev.crt -subj "/CN=*.localdev"
Here I used
*.localdev
so that I can use multiple subdomains such asapp1.localdev
,app2.localdev
, etc., and only need one certificate. Of course, you can also useyourdomain.local
directly. - Mounting in
docker-compose.yml
: Make sure that in yourdocker-compose.yml
,volumes
part of the Nginx service has mounts similar to./certs:/etc/nginx/certs
, and map the host's certificate file to the Nginx container. - Nginx configuration points to:
ssl_certificate
andssl_certificate_key
innginx.conf
point to the certificate path inside the container. -
hosts
file configuration: The last step, and the most forgettable step, is to pointyourdomain.local
(orapp.localdev
, etc.) to127.0.0.1
in your operating systemhosts
file. This way, when you access the domain name, the request will be hit to your local Docker container.
With this process, you can basically run a local development environment that supports HTTPS in five minutes. Although the browser will prompt that it is unsafe, it is completely fine in function and can also simulate HTTPS behavior in the production environment, such as handling redirects, security attributes of cookies ( Secure
, HttpOnly
), etc. It is also convenient to debug because you know that the certificate problem is not the "real" certificate problem like the production environment.
How to safely and effectively manage and update SSL certificates in a production environment?
The production environment cannot be as free as the development environment. What we are pursuing here is automation, safety and reliability. My personal most recommended solution is to combine Let's Encrypt and Certbot , and then Docker 's orchestration capabilities.
Let's Encrypt and Certbot: Let's Encrypt offers free SSL/TLS certificates, and Certbot is its officially recommended client tool that automatically acquires and renews certificates. Certbot supports a variety of web servers and challenge modes.
Certbot integration strategy in Docker:
Standalone Certbot container: This is my preferred way. You can run a standalone Certbot container that is responsible for obtaining and renewing certificates. This container needs to be able to access the
.well-known/acme-challenge
path (HTTP challenge) of the web server, or be able to modify the DNS record (DNS challenge).- HTTP Challenge: Certbot will temporarily create a file in your web root directory, and the Let's Encrypt server accesses this file through HTTP to verify domain name ownership. This means that your Nginx container needs to expose port 80, and the Certbot container needs to be able to write certificates to shared volumes that the Nginx container can access.
- DNS Challenge: Certbot will let you add a TXT record to the DNS record of the domain name. This method does not require the web server to expose port 80, and is more suitable for scenarios where the web service is not directly exposed, or scenarios where multiple services share a domain name. Many DNS service providers have Certbot plug-in to automate this process.
Nginx is shared as a reverse proxy and Certbot volume:
version: '3.8' services: nginx: image: nginx:alpine Ports: - "80:80" - "443:443" Volumes: - ./src:/var/www/html - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro - certbot-web:/var/www/certbot # HTTP challenge for Certbot- certbot-etc:/etc/letsencrypt # Certificate storage depends_on: - php restart: unless-stopped php: # ... (Similar to above) certbot: image: certbot/certbot Volumes: - certbot-web:/var/www/certbot - certbot-etc:/etc/letsencrypt # Command example, obtaining the certificate for the first time# command: certonly --webroot -w /var/www/certbot --email your_email@example.com -d yourdomain.com --agree-tos --no-eff-email # Command example, renewal certificate (usually run via cron job or docker-compose exec) # command: renew --webroot -w /var/www/certbot --post-hook "docker-compose exec nginx nginx -s reload" entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $!; done;'" # Automatic renewal restart: on-failure # If it fails, restart volumes: certbot-web: certbot-etc:
In Nginx configuration, you need to add a location block to the Certbot's verification path:
location /.well-known/acme-challenge/ { root /var/www/certbot; }
After running the Certbot command for the first time to obtain the certificate,
ssl_certificate
andssl_certificate_key
in the Nginx configuration should point to/etc/letsencrypt/live/yourdomain.com/fullchain.pem
and/etc/letsencrypt/live/yourdomain.com/privkey.pem
.Automated renewal: Let's Encrypt certificate is valid for only 90 days. Therefore, automated renewal is a must.
entrypoint
of the abovecertbot
service provides a simple automatic renewal mechanism, which attempts to renew every 12 hours. After a successful renewal, it is usually necessary to restart or reload the Nginx configuration to allow it to load the new certificate.--post-hook "docker-compose exec nginx nginx -s reload"
does this.-
Safety Considerations:
- Permissions: Make sure that the permissions of
certbot-etc
volume are set correctly and that only necessary processes can access the private key. - Backup: Back up the certificate and key in
certbot-etc
volume just in case. - Email Notifications: Certbot can configure email notifications, and you will receive a reminder when certificate renewal fails.
- Security of DNS Challenge: If using DNS Challenge, make sure your DNS API keys are stored securely, and do not hardcode them in your code.
- Permissions: Make sure that the permissions of
More advanced tools: For more complex production environments, consider using edge routers/reverse proxy like Traefik or Caddy . They have built-in Let's Encrypt support, which can automatically manage certificate acquisition and renewal, greatly simplifying HTTPS configuration. You only need to configure the domain name and they can automatically handle SSL, which is very worry-free. But if you are used to Nginx, the above Certbot solution is also powerful and flexible enough.
In short, the core of SSL configuration in production environments is automation and reliability, ensuring that certificates are always valid and can be automatically updated to avoid service interruptions due to certificate expiration.
The above is the detailed content of How to configure PHP environment with Docker to support SSL PHP container to enable HTTPS access method. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Create referrals table to record recommendation relationships, including referrals, referrals, recommendation codes and usage time; 2. Define belongsToMany and hasMany relationships in the User model to manage recommendation data; 3. Generate a unique recommendation code when registering (can be implemented through model events); 4. Capture the recommendation code by querying parameters during registration, establish a recommendation relationship after verification and prevent self-recommendation; 5. Trigger the reward mechanism when recommended users complete the specified behavior (subscription order); 6. Generate shareable recommendation links, and use Laravel signature URLs to enhance security; 7. Display recommendation statistics on the dashboard, such as the total number of recommendations and converted numbers; it is necessary to ensure database constraints, sessions or cookies are persisted,

cronisusedforpreciseschedulingonalways-onsystems,whileanacronensuresperiodictasksrunonsystemsthataren'tcontinuouslypowered,suchaslaptops;1.Usecronforexacttiming(e.g.,3AMdaily)viacrontab-ewithsyntaxMINHOURDOMMONDOWCOMMAND;2.Useanacronfordaily,weekly,o

Backdrop-filter is used to apply visual effects to the content behind the elements. 1. Use backdrop-filter:blur(10px) and other syntax to achieve the frosted glass effect; 2. Supports multiple filter functions such as blur, brightness, contrast, etc. and can be superimposed; 3. It is often used in glass card design, and it is necessary to ensure that the elements overlap with the background; 4. Modern browsers have good support, and @supports can be used to provide downgrade solutions; 5. Avoid excessive blur values and frequent redrawing to optimize performance. This attribute only takes effect when there is content behind the elements.

Define@keyframesbouncewith0%,100%attranslateY(0)and50%attranslateY(-20px)tocreateabasicbounce.2.Applytheanimationtoanelementusinganimation:bounce0.6sease-in-outinfiniteforsmooth,continuousmotion.3.Forrealism,use@keyframesrealistic-bouncewithscale(1.1

TobuildaDockerimagewithoutusingthecache,passthe--no-cacheflagtothedockerbuildcommand;thisensuresalllayersarerebuiltfromscratch,avoidingoutdateddependenciesorstalelayers,whichisusefulfordebugging,ensuringfreshpackageinstallations,achievingreproducible

There are three main ways to install software on Linux: 1. Use a package manager, such as apt, dnf or pacman, and then execute the install command after updating the source, such as sudoaptininstallcurl; 2. For .deb or .rpm files, use dpkg or rpm commands to install, and repair dependencies when needed; 3. Use snap or flatpak to install applications across platforms, such as sudosnapinstall software name, which is suitable for users who are pursuing version updates. It is recommended to use the system's own package manager for better compatibility and performance.

Use background-image and background-clip:text to achieve CSS text gradient effect; 2. You must set -webkit-background-clip:text and -webkit-text-fill-color:transparent to ensure browser compatibility; 3. You can customize linear or radial gradients, and it is recommended to use bold or large text to improve visual effect; 4. It is recommended to set color as an alternative color for unsupported environments; 5. Alternatives can use -webkit-mask-image to achieve more complex effects, but they are mainly suitable for advanced scenarios; this method is simple, has good compatibility and visual

AccessorsandmutatorsinLaravel'sEloquentORMallowyoutoformatormanipulatemodelattributeswhenretrievingorsettingvalues.1.Useaccessorstocustomizeattributeretrieval,suchascapitalizingfirst_nameviagetFirstNameAttribute($value)returningucfirst($value).2.Usem
