web php

HTTPS/TLS Basics

HTTPS is HTTP over TLS. TLS protects the connection between the client and the server by encrypting traffic, authenticating the server certificate, and reducing the chance that someone on the network can read or change requests.

PHP applications usually do not terminate TLS themselves. The web server, load balancer, CDN, or reverse proxy normally handles certificates and HTTPS. PHP still needs to understand whether the original request was secure because cookies, redirects, absolute URLs, and security headers depend on it.

Detecting HTTPS carefully

PHP example
<?php

declare(strict_types=1);

function isHttpsRequest(array $server, bool $trustProxyHeaders): bool
{
    if (($server['HTTPS'] ?? 'off') === 'on') {
        return true;
    }

    return $trustProxyHeaders
        && strtolower((string) ($server['HTTP_X_FORWARDED_PROTO'] ?? '')) === 'https';
}

echo isHttpsRequest(['HTTPS' => 'on'], false) ? 'secure' : 'not secure';
echo PHP_EOL;
echo isHttpsRequest(['HTTP_X_FORWARDED_PROTO' => 'https'], true) ? 'secure' : 'not secure';
echo PHP_EOL;

// Prints:
// secure
// secure

Only trust X-Forwarded-Proto or similar headers when they come from a trusted proxy that strips or replaces client-supplied values. A normal browser can send fake headers.

Why HTTPS matters to PHP

HTTPS affects:

  • secure cookies, especially session cookies
  • login and password forms
  • payment and personal data flows
  • OAuth callback URLs
  • absolute URL generation
  • mixed-content browser errors
  • HSTS and other security headers

If the application thinks a request is HTTP when the original browser request was HTTPS, it may generate insecure links or fail to mark cookies as Secure. If it blindly trusts forwarded headers, a client may trick the app about the scheme.

Redirecting HTTP to HTTPS

HTTP-to-HTTPS redirects are usually configured in the web server, CDN, or load balancer rather than inside every PHP controller. PHP can still enforce a redirect as a fallback.

PHP example
<?php

declare(strict_types=1);

function httpsUrl(string $host, string $uri): string
{
    return 'https://' . $host . $uri;
}

echo httpsUrl('example.com', '/account') . PHP_EOL;

// Prints:
// https://example.com/account

When building redirect URLs, use validated host configuration rather than blindly trusting the request Host header.

HSTS

After an application is reliably available over HTTPS, the edge server can send Strict-Transport-Security so browsers prefer HTTPS for future requests.

PHP example
<?php

declare(strict_types=1);

header('Strict-Transport-Security: max-age=31536000; includeSubDomains');

echo 'HSTS header prepared.' . PHP_EOL;

// Prints:
// HSTS header prepared.

Introduce HSTS deliberately. includeSubDomains affects subdomains too, and a long max age is difficult to undo quickly if a hostname cannot serve HTTPS correctly.

Outbound HTTPS Still Needs Verification

TLS also matters when PHP calls another service. An HTTP client should verify certificates and hostnames by default. Disabling verification to make a local error disappear turns a visible setup problem into a security problem.

When an outbound request fails, check the CA bundle, hostname, certificate chain, proxy setup, and system clock before weakening verification.

Common mistakes

  • Setting session cookies without the Secure attribute in production.
  • Trusting forwarded HTTPS headers from untrusted clients.
  • Generating http:// links on an HTTPS site.
  • Letting mixed-content assets load over HTTP.
  • Disabling certificate verification in HTTP clients to hide TLS errors.
  • Assuming local HTTP development means production can skip HTTPS.

What you should be able to do

After this lesson, you should be able to explain what HTTPS protects, identify where TLS is usually terminated, understand secure-cookie and HSTS implications, preserve certificate verification for outbound requests, and handle HTTPS detection carefully behind trusted proxies.

Practice

Task: Check Whether A Request Is Secure

Create a PHP helper that decides whether a request should be treated as HTTPS.

Requirements

  • Accept a server array and a boolean for whether proxy headers are trusted.
  • Treat HTTPS=on as secure.
  • Treat X-Forwarded-Proto=https as secure only when proxy headers are trusted.
  • Show one direct HTTPS case, one trusted proxy case, and one untrusted proxy case.
  • Add a short note explaining why this matters for secure cookies.

Check your work

The helper should avoid blindly trusting client-supplied forwarding headers.

Show solution
PHP example
<?php

declare(strict_types=1);

function isHttpsRequest(array $server, bool $trustProxyHeaders): bool
{
    if (($server['HTTPS'] ?? 'off') === 'on') {
        return true;
    }

    return $trustProxyHeaders
        && strtolower((string) ($server['HTTP_X_FORWARDED_PROTO'] ?? '')) === 'https';
}

$examples = [
    [['HTTPS' => 'on'], false],
    [['HTTP_X_FORWARDED_PROTO' => 'https'], true],
    [['HTTP_X_FORWARDED_PROTO' => 'https'], false],
];

foreach ($examples as [$server, $trustProxyHeaders]) {
    echo isHttpsRequest($server, $trustProxyHeaders) ? 'secure' : 'not secure';
    echo PHP_EOL;
}

// Prints:
// secure
// secure
// not secure

This matters for secure cookies because session and authentication cookies should only be sent over HTTPS in production. If the app misdetects HTTPS behind a proxy, it may fail to set Secure cookies or may trust forged headers from clients.