Skip to content
← Back to Functions
Code

Header Hardening Set

Sends standard hardening headers for content type, frame policy, referrer policy, and permissions policy.

Function signature

ogSendSecurityHeaders(options = array())

Categories

  • Security

Parameters

optionsHeader policy options. Recognized keys: `content_security_policy`, `dry_run`, `extra_headers`.

Return value

Short public-safe status message.

  • headers

Compatibility

Existing function name and call order preserved; metadata signature corrected to source.

Minimum PHP version: 7.4

Security notes

Validate request method, identity, permissions, and caller-owned allowlists before use; keep secrets out of public output.

Code

<?php

/*
 * Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
 * Free for personal and internal use. Paid project use requires visible credit
 * to Jeffery L. Paris. Corporate use requires a paid license fee unless a
 * separate written license states otherwise.
 */

/**
 * Sends standard hardening headers for content type, frame policy, referrer policy, and permissions policy.
 *
 * Primary use case: Global bootstrap/header layer.
 * Typical inputs: site policy options.
 * Typical output: sent headers.
 *
 * Implementation note: Do not send conflicting duplicate headers.
 *
 * @param array $options Header policy options.
 * @return array Structured result data with success, message, and data keys.
 */
function ogSendSecurityHeaders($options = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

	if (!is_array($options)) {
		$options = array();
	}

	$headers = array(
		'X-Content-Type-Options' => 'nosniff',
		'X-Frame-Options' => 'SAMEORIGIN',
		'Referrer-Policy' => 'strict-origin-when-cross-origin',
		'Permissions-Policy' => 'geolocation=(), microphone=(), camera=(), payment=()'
	);

	if (!empty($options['content_security_policy'])) {
		$headers['Content-Security-Policy'] = trim((string)$options['content_security_policy']);
	}

	if (!empty($options['extra_headers']) && is_array($options['extra_headers'])) {
		foreach ($options['extra_headers'] as $header_name => $header_value) {
			$header_name = trim((string)$header_name);
			$header_value = trim((string)$header_value);

			if (preg_match('/^[A-Za-z0-9-]+$/', $header_name) && strpos($header_value, "\n") === false && strpos($header_value, "\r") === false) {
				$headers[$header_name] = $header_value;
			}
		}
	}

	$sent = array();
	$dry_run = false;
	if (!empty($options['dry_run'])) {
		$dry_run = true;
	}

	if (!$dry_run && headers_sent()) {
		$result['message'] = 'Headers were already sent.';
		$result['data'] = array('headers' => $headers, 'sent' => $sent);
		return $result;
	}

	foreach ($headers as $header_name => $header_value) {
		if (!$dry_run) {
			header($header_name . ': ' . $header_value, true);
		}
		$sent[] = $header_name;
	}

	$result['success'] = true;
	if ($dry_run) {
		$result['message'] = 'Security headers prepared.';
	} else {
		$result['message'] = 'Security headers sent.';
	}
	$result['data'] = array(
		'headers' => $headers,
		'sent' => $sent,
		'dry_run' => $dry_run
	);

	return $result;
}