# Configuring TLS and HTTPS Correctly

> TLS configuration for web servers per BSI TR-02102 and TLSRef: protocols, cipher suites, certificates and check commands for Nginx, Apache and PHP clients.

Source: https://phpsos.de/en/blog/tls-https-richtig-konfigurieren/  
As of: 25/10/2026  
Provider: PHP SOS, PHP emergency help and server security. Immediate help via WhatsApp: https://wa.me/491736494914?text=Hello%20PHP%20SOS%2C%20we%20need%20help%20with%20our%20PHP%20server.%20Company%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**In short:** A solid TLS configuration for web servers offers only TLS 1.2 and TLS 1.3, uses cipher suites with forward secrecy and authenticated encryption (AES-GCM, ChaCha20-Poly1305), and a certificate with an ECDSA key or an RSA key of sufficient length that renews automatically. Guidance comes from the BSI guideline TR-02102-2 and the Server-Side TLS recommendations of TLSRef. Check the result from the outside, and do not forget the connections that PHP itself establishes.

## Why Old TLS Settings Survive for So Long

The padlock in the browser is there, so encryption counts as done. That is how many operators think, which is why the TLS configuration often stays unchanged for years. It then dates from the time the server was set up, from a copied template or from a panel's default values.

The problem: recommendations change. Protocol versions are superseded, mechanisms are considered weak, new key exchange methods are added. The BSI (German Federal Office for Information Security) most recently published TR-02102 in version 2026-01 on 27 January 2026 and already points there to hybrid post-quantum mechanisms for TLS 1.3. Anyone who has not looked at their settings for years is very likely no longer up to date.

If you are reviewing servers after PHP2Shell anyway, you can look at the TLS configuration in the same pass. It does not close the PHP vulnerability, but it belongs to any proper hardening.

## What a Good TLS Configuration for Web Servers Is Based On

Two sources are particularly useful for operators in Europe. **BSI TR-02102** evaluates cryptographic mechanisms; part 2 deals with TLS. It is aimed mainly at developers and planners and is often the benchmark for public authorities and regulated sectors. The **Server-Side TLS recommendations**, formerly maintained in the Mozilla wiki, are now continued under TLSRef and provide concrete profiles that can be copied.

In version 6.0, TLSRef distinguishes two profiles:

| Feature | Intermediate (for most websites) | Modern |
|---|---|---|
| Protocols | TLS 1.2 and TLS 1.3 | TLS 1.3 only |
| Cipher suites TLS 1.2 | ECDHE with AES-GCM or ChaCha20-Poly1305 | none |
| Key exchange groups | X25519MLKEM768, X25519, prime256v1, secp384r1 | as Intermediate |
| Certificate | ECDSA P-256 recommended or RSA 2048 bit | ECDSA P-256 |
| Certificate validity | 90 days recommended, at most 366 days | 90 days |
| HSTS max-age | two years | two years |

For public websites, Intermediate is the usual starting point. The profile excludes very old devices but covers current browsers and operating systems. The hybrid group X25519MLKEM768 requires a current TLS library. Check whether your OpenSSL version supports it before you enter it.

## Nginx and Apache: A Secure Starting Point

The following examples implement the Intermediate profile. Paths are placeholders; the list of cipher suites applies to TLS 1.2, while TLS 1.3 brings its own secure default mechanisms.

```nginx
server {
    listen 443 ssl;
    server_name www.example.de;

    ssl_certificate     /etc/ssl/example/fullchain.pem;
    ssl_certificate_key /etc/ssl/example/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers off;
}

server {
    listen 80;
    server_name www.example.de;
    return 301 https://$host$request_uri;
}
```

```apache
SSLProtocol             -all +TLSv1.2 +TLSv1.3
SSLCipherSuite          ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
SSLHonorCipherOrder     off
```

The second `server` block permanently redirects unencrypted requests to HTTPS. The redirect should point directly to the final address and not run through several intermediate stops. Afterwards, also check embedded resources: images, scripts or fonts that are still loaded via HTTP trigger warnings in the browser or are blocked.

The setting that lets the client choose the order matches the TLSRef recommendation: all offered mechanisms are considered secure, and devices without AES acceleration can thus prefer ChaCha20. After every change, test the configuration (`nginx -t` or `apachectl configtest`) and reload the service. The matching HSTS header and further browser directives are described in the guide [Security Headers for Websites](https://phpsos.de/en/ratgeber/sicherheits-header/).

> **Tip:**
> Panels such as Plesk or cPanel often overwrite your own changes to generated files. Set TLS directives there through the panel's intended settings or templates.

## Certificates: Keys, Chain, Renewal

A large share of TLS problems in practice have nothing to do with cipher suites but with certificates. Typical errors are an incomplete chain in which the intermediate certificate is missing, expired certificates on secondary domains, and private keys with file permissions that are too broad.

Three rules help:

1. **Renew automatically** and monitor the renewal. Short validity periods, as TLSRef recommends them, are practical only with automation.
2. **Protect private keys:** readable only by root and the web server process, never in the web root, never in a repository.
3. **Record all names:** main domain, www, subdomains for shop, staging, webmail interface and panels.

Anyone who uses certificates in several places, such as on CDN, load balancer and origin server, should keep an overview: which certificate is installed where, who renews it and when it expires. Without this overview, an expired certificate is often noticed only when customers get in touch.

## How to Check Your Configuration from Outside

The following commands are pure checks and change nothing. Replace the hostname with your own:

```bash
# TLS 1.3 and TLS 1.2 should work
openssl s_client -connect www.example.de:443 -servername www.example.de -tls1_3 </dev/null
openssl s_client -connect www.example.de:443 -servername www.example.de -tls1_2 </dev/null
# TLS 1.1 should fail
openssl s_client -connect www.example.de:443 -servername www.example.de -tls1_1 </dev/null
# Validity and owner of the delivered certificate
openssl s_client -connect www.example.de:443 -servername www.example.de </dev/null 2>/dev/null | openssl x509 -noout -subject -dates
```

A failure in the TLS 1.1 test may also be due to your local OpenSSL version no longer offering the old protocol at all. For a complete report on all offered mechanisms, a specialized TLS scanner is suitable. Check each subdomain individually, because they often run with their own settings.

## Forgotten: TLS Where PHP Itself Is the Client

PHP applications themselves establish many connections: to payment providers, shipping providers, interfaces, mail servers and databases. Here the most common mistake is not a weak setting but a disabled certificate check, often as a quick fix for a problem during development.

```php
<?php
// Secure: certificate and hostname are verified (keep the default behavior)
$ch = curl_init('https://api.partner.example/v1/status');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_TIMEOUT        => 10,
]);
$antwort = curl_exec($ch);
```

Search the code for `CURLOPT_SSL_VERIFYPEER` set to `false` and for `verify_peer` in stream contexts. Every match practically cancels the protection of TLS for that connection. Where services sit behind a CDN, the leg between CDN and origin server should also use a valid, verified certificate. Further web server settings for PHP setups can be found under [Configuring Nginx for PHP Securely](https://phpsos.de/en/blog/nginx-fuer-php-absichern/), benchmarks for the entire hardening under [CIS Benchmarks and BSI Modules](https://phpsos.de/en/blog/cis-benchmarks-bsi-bausteine/).

## How does PHP SOS help?

- We record all hostnames and services with TLS, including panels, staging and webmail interfaces.
- We compare the settings with TR-02102 and the matching TLSRef profile and document deliberate deviations.
- We switch certificates to automatic renewal with monitoring.
- We search the application code for disabled certificate checks and show the fix.

TLS is one building block of our [server hardening for PHP web servers](https://phpsos.de/en/leistungen/server-haertung/); the connection to the current situation is explained in [What Is PHP2Shell?](https://phpsos.de/en/php2shell/was-ist-php2shell/).


## Frequently asked questions

**Do I still have to offer TLS 1.0 and TLS 1.1?**  
As a rule, no. Current recommendations such as the Intermediate profile of TLSRef set TLS 1.2 as the lower limit. Anyone who needs older protocols for legacy devices should make that a deliberate decision, document it and restrict it to a separate service.

**Is a free certificate less secure than a purchased one?**  
For the encryption itself it makes no difference. What matters are key type, key length, a complete certificate chain and reliable automatic renewal. Differences exist in the scope of validation, validity period and the provider's service.

**Is HTTPS between visitor and CDN enough?**  
No. The connection between CDN and origin server should also be encrypted and should verify the origin's certificate. Otherwise data travels over the second leg unencrypted or unverified.

**What does TLS have to do with PHP2Shell?**  
Nothing directly. TLS protects data in transit, whereas PHP2Shell affects the PHP interpreter on the server. An encrypted connection also carries attack requests reliably, which is why the PHP update remains mandatory regardless.

## Sources

- [BSI TR-02102: Cryptographic Mechanisms, Part 2 TLS (German Federal Office for Information Security)](https://www.bsi.bund.de/DE/Themen/Unternehmen-und-Organisationen/Standards-und-Zertifizierung/Technische-Richtlinien/TR-nach-Thema-sortiert/tr02102/tr-02102.html)
- [TLSRef: Server-Side TLS Guidelines](https://docs.tlsref.org/)
- [Nginx documentation](https://nginx.org/en/docs/)
- [Apache HTTP Server: documentation](https://httpd.apache.org/docs/)
