{ http_port {{ caddy_http_port }} https_port {{ caddy_https_port }} admin off persist_config off {% if caddy_default_bind != None %} default_bind {{ caddy_default_bind }} {% endif %} {% if caddy_crowdsec %} order crowdsec first {% endif %} {% if caddy_geoip %} order geoip2_vars first {% endif %} {% if caddy_coraza %} order coraza_waf first {% endif %} {% if caddy_consul %} # See: https://github.com/pteich/caddy-tlsconsul storage consul { address "{{ caddy_consul_server }}" token "{{ caddy_out_consul_token.token.SecretID | mandatory }}" timeout 10 prefix "caddy-{{ caddy_my_name }}-tls" value_prefix "{{ caddy_my_name }}" aes_key "{{ caddy_consul_aes_key | mandatory }}" tls_enabled "true" tls_insecure "false" } {% endif %} {% if caddy_geoip %} # See: https://github.com/zhangjiayin/caddy-geoip2 geoip2 { accountId "{{ caddy_geoip_account_id | mandatory }}" licenseKey "{{ caddy_geoip_license_key | mandatory }}" databaseDirectory "{{ caddy_home_dir }}/geoip2" lockFile "{{ caddy_home_dir }}/geoip2/.geoip2.lock" editionID "{{ caddy_geoip_edition_ids }}" updateUrl "https://updates.maxmind.com" updateFrequency {{ caddy_geoip_update_frequency }} } {% endif %} {% if caddy_email | default(None) != None %} email "{{ caddy_email }}" {% endif %} {% if caddy_crowdsec %} # See: https://github.com/hslatman/caddy-crowdsec-bouncer crowdsec { api_key {{ caddy_crowdsec_api_key | mandatory }} api_url {{ caddy_crowdsec_api_url }} ticker_interval {{ caddy_crowdsec_ticker_interval }}s {% if caddy_crowdsec_appsec_url != None %} appsec_url {{ caddy_crowdsec_appsec_url }} {% endif %} {% if not caddy_crowdsec_streaming %} disable_streaming {% endif %} {% if caddy_crowdsec_hard_fails %} enable_hard_fails {% endif %} } {% endif %} {% if caddy_coraza %} log waf { include "http.handlers.waf" format {{ caddy_log_format }} format append { waf_id {{ caddy_my_name }} } {% if caddy_log_to_file %} output file {{ caddy_log_dir }}/waf.log {% else %} output stdout {% endif %} } {% endif %} {% if caddy_log_debug %} log { level DEBUG format {{ caddy_log_format }} format append { waf_id {{ caddy_my_name }} } output file {{ caddy_log_dir }}/debug.log } {% endif %} } # 0=origin, 1=methods, 2=headers, 3=allow credentials, 4=max age, 5=vary, 6=expose headers (cors) { @match-cors-preflight-{args[0]} { header Origin {args[0]} method OPTIONS } header @match-cors-preflight-{args[0]} { Access-Control-Allow-Origin "{args[0]}" Access-Control-Allow-Methods "{args[1]}" Access-Control-Allow-Headers "{args[2]}" Access-Control-Allow-Credentials "{args[3]}" Access-Control-Max-Age "{args[4]}" Vary "{args[5]}" } handle @match-cors-preflight-{args[0]} { respond "" 204 } @match-cors-request-{args[0]} { header Origin {args[0]} not method OPTIONS } header @match-cors-request-{args[0]} { >Access-Control-Allow-Origin "{args[0]}" >Access-Control-Allow-Credentials "{args[3]}" >Vary "{args[5]}" >Access-Control-Expose-Headers "{args[6]}" } } (cors-deny) { @cors-preflight-deny method OPTIONS handle @cors-preflight-deny { respond "" 403 } } {% for site in caddy_sites %} {% if (site.site_aliases_redirect | default(caddy_sites_defaults.site_aliases_redirect)) and (site.site_aliases | default([]) | length) > 0 %} {{ site.site_aliases | join(', ') }} { redir https://{{ site.name }}{uri} } {% else %} {% for alias in site.site_aliases | default(caddy_sites_defaults.site_aliases) %} {{ alias }}, {% endfor %} {% endif %} {{ site.name }} { encode gzip zstd {% if caddy_crowdsec and site.crowdsec | default(caddy_sites_defaults.crowdsec) %} crowdsec {% endif %} {% for path in site.paths %} handle{% if path.strip_prefix | default(false) %}_path{% endif %} {{ path.path | default('*') }} { reverse_proxy { to {{ path.addrs | join(' ') }} header_up X-Real-IP {remote} lb_policy {{ site.proxy_lb_policy | default(caddy_sites_defaults.proxy_lb_policy) }} {% if site.proxy_active_checks | default(caddy_sites_defaults.proxy_active_checks) %} health_uri {{ site.proxy_active_checks_uri | default(caddy_sites_defaults.proxy_active_checks_uri) }} health_port {{ site.proxy_active_checks_port | default(caddy_sites_defaults.proxy_active_checks_port) }} health_interval {{ site.proxy_active_checks_interval | default(caddy_sites_defaults.proxy_active_checks_interval) }} health_timeout {{ site.proxy_active_checks_timeout | default(caddy_sites_defaults.proxy_active_checks_timeout) }} health_method {{ site.proxy_active_checks_method | default(caddy_sites_defaults.proxy_active_checks_method) }} health_status {{ site.proxy_active_checks_status | default(caddy_sites_defaults.proxy_active_checks_status) }} {% if site.proxy_active_checks_body | default(caddy_sites_defaults.proxy_active_checks_body) != None %} health_body {{ site.proxy_active_checks_body | default(caddy_sites_defaults.proxy_active_checks_body) }} {% endif %} {% endif %} {% if site.proxy_passive_checks | default(caddy_sites_defaults.proxy_passive_checks) %} fail_duration {{ site.proxy_passive_checks_fail_duration | default(caddy_sites_defaults.proxy_passive_checks_fail_duration) }} max_fails {{ site.proxy_passive_checks_max_fails | default(caddy_sites_defaults.proxy_passive_checks_max_fails) }} unhealthy_status {{ site.proxy_passive_checks_unhealthy_status | default(caddy_sites_defaults.proxy_passive_checks_unhealthy_status) }} unhealthy_latency {{ site.proxy_passive_checks_unhealthy_latency | default(caddy_sites_defaults.proxy_passive_checks_unhealthy_latency) }} {% if site.proxy_passive_checks_unhealthy_request_count | default(caddy_sites_defaults.proxy_passive_checks_unhealthy_request_count) != None %} unhealthy_request_count {{ site.proxy_passive_checks_unhealthy_request_count | default(caddy_sites_defaults.proxy_passive_checks_unhealthy_request_count) }} {% endif %} {% endif %} } } {% endfor %} {% if site.custom_cert | default (caddy_sites_defaults.custom_cert) %} tls {{ site.custom_cert_file }} {{ site.custom_cert_key_file }} {% endif %} {% if site.filter_blacklist | default(caddy_sites_defaults.filter_blacklist) | length > 0 %} @blocked { {% for ip in site.filter_blacklist | default(caddy_sites_defaults.filter_blacklist) %} remote_ip {{ ip }} {% endfor %} } respond @blocked "Access Denied" 403 {% endif %} {% if caddy_geoip and site.geoip | default(caddy_sites_defaults.geoip) %} geoip2_vars strict @geofilter expression {{ site.geoip_filter_expression | default(caddy_sites_defaults.geoip_filter_expression) }} respond @geofilter "Access Denied" 403 {% if site.geoip_debug | default(caddy_sites_defaults.geoip_debug) %} header x-geoip-is-anonymous "{geoip2.is_anonymous}" header x-geoip-is-anonymous-vpn "{geoip2.is_anonymous_vpn}" header x-geoip-is-hosting-provider "{geoip2.is_hosting_provider}" header x-geoip-is-public-proxy "{geoip2.is_public_proxy}" header x-geoip-is-residential-proxy "{geoip2.is_residential_proxy}" header x-geoip-is-tor-exit-node "{geoip2.is_tor_exit_node}" header x-geoip-connection-type "{geoip2.connection_type}" header x-geoip-domain "{geoip2.domain}" header x-geoip-country-code "{geoip2.country_code}" # header x-geoip-country-confidence "{geoip2.country_confidence}" header x-geoip-country-eu "{geoip2.country_eu}" header x-geoip-country-geoname-id "{geoip2.country_geoname_id}" header x-geoip-country-name "{geoip2.country_name}" header x-geoip-continent-code "{geoip2.continent_code}" header x-geoip-continent-geoname-id "{geoip2.continent_geoname_id}" header x-geoip-continent-name "{geoip2.continent_name}" # header x-geoip-city-confidence "{geoip2.city_confidence}" header x-geoip-city-geoname-id "{geoip2.city_geoname_id}" header x-geoip-city-name "{geoip2.city_name}" header x-geoip-location-time-zone "{geoip2.location_time_zone}" header x-geoip-autonomous-system-number "{geoip2.autonomous_system_number}" header x-geoip-autonomous-system-organization "{geoip2.autonomous_system_organization}" header x-geoip-isp "{geoip2.isp}" header x-geoip-mobile-country-code "{geoip2.mobile_country_code}" header x-geoip-mobile-network-code "{geoip2.mobile_network_code}" header x-geoip-organization "{geoip2.organization}" header x-geoip-ip-address "{geoip2.ip_address}" {% endif %} {% endif %} @not_whitelisted not { {% for ip in site.filter_whitelist | default(caddy_sites_defaults.filter_whitelist) %} remote_ip {{ ip }} {% endfor %} } {% if caddy_ratelimit and site.rate_limit | default(caddy_sites_defaults.rate_limit) %} # See: https://github.com/mholt/caddy-ratelimit rate_limit @not_whitelisted { {% if caddy_consul %} distributed storage consul { address "{{ caddy_consul_server }}" token "{{ caddy_out_consul_token.token.SecretID }}" timeout 10 prefix "caddy-{{ caddy_my_name }}-rl" value_prefix "{{ caddy_my_name }}" aes_key "{{ caddy_consul_aes_key }}" tls_enabled "true" tls_insecure "false" } {% endif %} log_key zone remote_ip { key {remote.ip} events {{ site.rate_limit_events | default(caddy_sites_defaults.rate_limit_events) }} window {{ site.rate_limit_window | default(caddy_sites_defaults.rate_limit_window) }} } } {% endif %} {% if caddy_coraza and site.crs | default(caddy_sites_defaults.crs) %} coraza_waf @not_whitelisted { load_owasp_crs directives ` Include "{{ caddy_sites_dir }}/{{ site.id }}/coraza.conf" Include "{{ caddy_sites_dir }}/{{ site.id }}/crs-setup.conf" {% for plugin in site.crs_plugins | default(caddy_sites_defaults.crs_plugins) %} Include "{{ caddy_crs_plugins_dir }}/{{ plugin }}-config.conf" Include "{{ caddy_crs_plugins_dir }}/{{ plugin }}-before.conf" {% endfor %} Include "{{ caddy_sites_dir }}/{{ site.id }}/exclusions-request-before.conf" Include "{{ caddy_config_dir }}/coreruleset-{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}/rules/*.conf" Include "{{ caddy_sites_dir }}/{{ site.id }}/exclusions-response-after.conf" {% for plugin in site.crs_plugins | default(caddy_sites_defaults.crs_plugins) %} Include "{{ caddy_crs_plugins_dir }}/{{ plugin }}-after.conf" {% endfor %} SecRuleEngine On ` } {% endif %} {% if site.cors | default(caddy_sites_defaults.cors) %} {% for origin in site.cors_allowed_origins | mandatory %} import cors https://{{ origin }} "{{ site.cors_allowed_methods | default(caddy_sites_defaults.cors_allowed_methods) | listadddel(site.cors_allowed_methods_add | default([]), site.cors_allowed_methods_del | default([])) | join(', ') }}" "{{ site.cors_allowed_headers | default(caddy_sites_defaults.cors_allowed_headers) | listadddel(site.cors_allowed_headers_add | default([]), site.cors_allowed_headers_del | default([])) | join(', ') }}" "{{ site.cors_allowed_credentials | default(caddy_sites_defaults.cors_allowed_credentials) | ternary('true', 'false') }}" "{{ site.cors_max_age | default(caddy_sites_defaults.cors_max_age) }}" "{{ site.cors_vary | default(caddy_sites_defaults.cors_vary) | listadddel(site.cors_vary_add | default([]), site.cors_vary_del | default([])) | join(', ') }}" "{{ site.cors_exposed_headers | default(caddy_sites_defaults.cors_exposed_headers) | listadddel(site.cors_exposed_headers_add | default([]), site.cors_exposed_headers_del | default([])) | join(', ') }}" {% endfor %} import cors-deny {% endif %} {% if caddy_bot_barrier and site.bot_barrier | default(caddy_sites_defaults.bot_barrier) %} # See: https://github.com/steffenbusch/caddy-bot-barrier bot_barrier @not_whitelisted { secret {{ site.bot_barrier_secret | default(caddy_sites_defaults.bot_barrier_secret) | mandatory }} complexity {{ site.bot_barrier_complexity | default(caddy_sites_defaults.bot_barrier_complexity) }} valid_for {{ site.bot_barrier_valid_for | default(caddy_sites_defaults.bot_barrier_valid_for) }} seed_cookie_name __chall_{{ site.id }}_seed solution_cookie_name __chall_{{ site.id }}_solution mac_cookie_name __chall_{{ site.id }}_mac template {{ caddy_config_dir }}/bot_barrier_template.html } {% endif %} {% if site.header | default(caddy_sites_defaults.header) %} header { Content-Security-Policy "{{ caddy_sites_defaults.header_content_security_policy | combine(site.header_content_security_policy | default({})) | dict2str(sep1=' ', sep2='; ', sep3=';') }}" Cross-Origin-Embedder-Policy "{{ site.header_cross_origin_embedder_policy | default(caddy_sites_defaults.header_cross_origin_embedder_policy) }}" Cross-Origin-Opener-Policy "{{ site.header_cross_origin_opener_policy | default(caddy_sites_defaults.header_cross_origin_opener_policy) }}" Cross-Origin-Resource-Policy "{{ site.header_cross_origin_resource_policy | default(caddy_sites_defaults.header_cross_origin_resource_policy) }}" Feature-Policy "{{ caddy_sites_defaults.header_feature_policy | combine(site.header_feature_policy | default({})) | dict2str(sep1=' ', sep2='; ', sep3=';') }}" Permissions-Policy "{{ caddy_sites_defaults.header_permissions_policy | combine(site.header_permissions_policy | default({})) | dict2str(sep2=', ') }}" Referrer-Policy "{{ site.header_referrer_policy | default(caddy_sites_defaults.header_referrer_policy) }}" Strict-Transport-Security "{{ site.header_strict_transport_security | default(caddy_sites_defaults.header_strict_transport_security) }}" X-Content-Type-Options "{{ site.header_x_content_type_options | default(caddy_sites_defaults.header_x_content_type_options) }}" X-Frame-Options "{{ site.header_x_frame_options | default(caddy_sites_defaults.header_x_frame_options) }}" X-Robots-Tag "{{ site.header_x_robots_tag | default(caddy_sites_defaults.header_x_robots_tag) }}" ?Content-Type "{{ site.header_default_content_type | default(caddy_sites_defaults.header_default_content_type) }}" >Set-Cookie "(.*)" "$1; {{ site.header_cookies_attributes | default(caddy_sites_defaults.header_cookies_attributes) }}" {% for header in site.header_delete | default(caddy_sites_defaults.header_delete) %} -{{ header }} {% endfor %} {% for header in site.header_add | default(caddy_sites_defaults.header_add) %} {{ header.name }} "{{ header.value }}" {% endfor %} } {% endif %} handle_errors 403 { header X-Blocked "true" respond "Your request was blocked. 🔴" } log { level INFO format {{ caddy_log_format }} format append { waf_id {{ caddy_my_name }} } {% if caddy_log_to_file %} output file {{ caddy_log_dir }}/site_{{ site.id }}.log {% else %} output stdout {% endif %} } } {% endfor %}