{"id":18494915,"url":"https://github.com/qaware/x-forwarded-filter","last_synced_at":"2025-04-08T22:31:37.893Z","repository":{"id":57730848,"uuid":"111925907","full_name":"qaware/x-forwarded-filter","owner":"qaware","description":"x-forwared filter for the masses","archived":false,"fork":false,"pushed_at":"2020-02-14T10:19:31.000Z","size":449,"stargazers_count":18,"open_issues_count":0,"forks_count":5,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-23T19:11:08.898Z","etag":null,"topics":["forwarded","rfc-7239","rfc7239","servlet-filter","x-forwarded","x-forwarded-for","x-forwarded-host","x-forwarded-port","x-forwarded-prefix","x-forwarded-proto","xforwarded"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qaware.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-11-24T14:17:04.000Z","updated_at":"2024-08-08T15:01:49.000Z","dependencies_parsed_at":"2022-09-26T22:01:37.715Z","dependency_job_id":null,"html_url":"https://github.com/qaware/x-forwarded-filter","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qaware%2Fx-forwarded-filter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qaware%2Fx-forwarded-filter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qaware%2Fx-forwarded-filter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qaware%2Fx-forwarded-filter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qaware","download_url":"https://codeload.github.com/qaware/x-forwarded-filter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247940359,"owners_count":21021954,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["forwarded","rfc-7239","rfc7239","servlet-filter","x-forwarded","x-forwarded-for","x-forwarded-host","x-forwarded-port","x-forwarded-prefix","x-forwarded-proto","xforwarded"],"created_at":"2024-11-06T13:22:42.769Z","updated_at":"2025-04-08T22:31:36.683Z","avatar_url":"https://github.com/qaware.png","language":"Java","readme":"[![Build Status](https://travis-ci.org/qaware/x-forwarded-filter.svg?branch=master)](https://travis-ci.org/qaware/x-forwarded-filter) [![sonarcloud](https://sonarcloud.io/api/project_badges/measure?project=de.qaware.xff%3Ax-forwarded-filter\u0026metric=alert_status)](https://sonarcloud.io/dashboard?id=de.qaware.xff%3Ax-forwarded-filter) [![Coverage Status](https://coveralls.io/repos/github/qaware/x-forwarded-filter/badge.svg?branch=master)](https://coveralls.io/github/qaware/x-forwarded-filter?branch=master) [![License](https://img.shields.io/badge/license-APACHE2.0-green.svg?style=flat)]() [![Download](https://api.bintray.com/packages/qaware-oss/maven/x-forwarded-filter/images/download.svg)](https://bintray.com/qaware-oss/maven/x-forwarded-filter/_latestVersion)\n\n\n# Standalone (x-)forwarded* Filter\n\nThe (x-)forwarded* Http-Headers family are a pseudo standard with varying and mostly lacking support in most proxies, frameworks and webservers.\n\nThis filter supports the following Http Headers:\n-  `Forwarded` [as of RFC 7239](http://tools.ietf.org/html/rfc7239)\n- or if a `Forwarded` header is NOT found:\n  - `X-Forwarded-Host`\n  - `X-Forwarded-Port`\n  - `X-Forwarded-Proto`\n- Additionally for both cases: `X-Forwarded-Prefix` is supported to adapt the `getContextPath` result.\n\n**Features:**\n- Supports adapting  `HttpServletRequest`'s  scheme, host, port and prefix(contextPath)by replacing them transparently using the information from (x-)forwarded* http header(s).\n- Supports `HttpServletResponse.sendRedirect(location)` by rewriting it accordingly\n- Processing of headers and extracting parts (e.g. form `Forwarded` ) is case insensitive\n  - valid: X-Forwarded-Host, x-forwarded-host, X-forwarded-HOST,..\n- Supports multiple headers of same name in request -\u003e use Find-First-Strategy\n  - e.g.\u003cbr/\u003e\n    X-Forwarded-Host: \"hostA\"\u003cbr/\u003e\n    X-Forwarded-Host: \"hostB\"   =\u003e filter will use \"hostA\" \n- Supports multiple COMMA-SPACE separated values inside a headers value -\u003euse Find-First-Strategy\n  - e.g. X-Forwarded-Host: \"hostA, hostB\"  =\u003e filter will use \"hostA\"\n- Configurable processing strategy for `X-Forwarded-Prefix` =\u003e  PREPEND or REPLACE the contextPath\n  `ForwardedFilter.xForwardedPrefixStrategy=[PREPEND, REPLACE]`\n- Configurable header processing and removal strategy\n  `ForwardedFilter.headerProcessingStrategy=[EVAL_AND_KEEP, EVAL_AND_REMOVE, DONT_EVAL_AND_REMOVE]`\n  - EVAL_AND_KEEP - process headers and keep them in the list of headers for downstream processing\n  - EVAL_AND_REMOVE - process headers and remove them. Wont be visible any more when accessing getHeader(s)\n  - DONT_EVAL_AND_REMOVE - don't process the headers, just remove them.\n  - ~~DONT_EVAL_AND_DONT_REMOVE~~ =\u003e just don't activate this filter - same effect\n   \n  \n# Table of contents\n- [Standalone (x-)forwarded* Filter](#standalone-x-forwarded-filter)\n- [Usage](#usage)\n  - [Dependencies](#dependencies)\n    - [Maven](#maven)\n    - [Gradle](#gradle)\n  - [SpringBoot](#springboot)\n  - [web.xml (e.g. Websphere Liberty or Spring)](#webxml-eg-websphere-liberty-or-spring)\n  - [Disable other (x-)forwarded* header processing in various products](#disable-other-x-forwarded-header-processing-in-various-products)\n    - [Websphere liberty](#websphere-liberty)\n    - [Spring](#spring)\n    - [Tomcat](#tomcat)\n- [What? Why? X-forwarded?](#what-why-x-forwarded)\n  - [Why do I need (x-)forwarded](#why-do-i-need-x-forwarded)\n  - [Why should i use THIS filter](#why-should-i-use-this-filter)\n  - [What this filter is not](#what-this-filter-is-not)\n  - [(x-)forwarded* support in various products](#x-forwarded-support-in-various-products)\n  - [X-Forwarded headers overview and explanation](#x-forwarded-headers-overview-and-explanation)\n  - [Developer Info](#developer-info)\n    - [Implementation Details](#implementation-details)\n    - [How to build](#how-to-build)\n- [Appendix](#appendix)\n  - [Maintainer](#maintainer)\n  - [Credits](#credits)\n  - [License](#license)\n\n# Usage\nYou probably should disable all other x-forwarded processing code - like done by your underlying webserver. See: [Disable other (x-)forwarded* header processing in various products](#disable-other-x-forwarded-header-processing-in-various-products)\n\n## Dependencies\n- Only slf4j, commons-lang3 and commons-collections4\n- No Spring required\n\nThe JARs are available via Maven Central and JCenter.\n\n### Maven\nIf you are using Maven to build your project, add the following to the `pom.xml` file.\n```XML\n\u003c!-- https://mvnrepository.com/artifact/de.qaware.xff/x-forwarded-filter --\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003ede.qaware.xff\u003c/groupId\u003e\n    \u003cartifactId\u003ex-forwarded-filter\u003c/artifactId\u003e\n    \u003cversion\u003e1.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Gradle\nIn case you are using Gradle to build your project, add the following to the `build.gradle` file:\n```groovy\nrepositories {\n    jcenter()\n    mavenCentral()\n}\n\ndependencies {\n    // https://mvnrepository.com/artifact/de.qaware.xff/x-forwarded-filter\n    compile group: 'de.qaware.xff', name: 'x-forwarded-filter', version: '1.0'\n}\n```\n\n## SpringBoot\nSimple:\n```java\n    @Bean\n    FilterRegistrationBean forwardedHeaderFilter() {\n        FilterRegistrationBean frb = new FilterRegistrationBean();\n        frb.setFilter(new ForwardedHeaderFilter());\n        //must run as first filter\n        frb.setOrder(Ordered.HIGHEST_PRECEDENCE);\n        //Configuration options and their defaults\n        frb.addInitParameter(ForwardedHeaderFilter.ENABLE_RELATIVE_REDIRECTS_INIT_PARAM, Boolean.FALSE.toString());//false is default\n        frb.addInitParameter(ForwardedHeaderFilter.HEADER_PROCESSING_STRATEGY, HeaderProcessingStrategy.EVAL_AND_REMOVE.name());//EVAL_AND_REMOVE is default\n        frb.addInitParameter(ForwardedHeaderFilter.X_FORWARDED_PREFIX_STRATEGY, XForwardedPrefixStrategy.REPLACE.name()); //Replace is default\n        return frb;\n    }\n}\n```\n\nExtended Configuration:\n```java\nimport de.qaware.xff.filter.ForwardedHeaderFilter;  //warning! dont trust the autoimport as it will likley use org.springframework.web.filter.ForwardedHeaderFilter \n//..\n@Configuration\n@ConditionalOnProperty(value = \"de.qaware.xff.enabled\", havingValue = \"true\")\npublic class FilterRegistrationConfiguration {\n\n\t@Data\n\t@Configuration\n\t@ConfigurationProperties(prefix = \"de.qaware.xff\")\n\tstatic class ForwardedHeaderFilterConfiguration{\n\t\tprivate boolean enabled = true;\n\t\tprivate boolean enableRelativeRedirects = false;\n\t\tprivate HeaderProcessingStrategy headerProcessingStrategy = HeaderProcessingStrategy.EVAL_AND_KEEP;\n\t\tprivate XForwardedPrefixStrategy xForwardedPrefixStrategy = XForwardedPrefixStrategy.PREPEND;\n\t}\n\n\n\t@Bean\n\tFilterRegistrationBean forwardedHeaderFilter(ForwardedHeaderFilterConfiguration c) {\n\t\tFilterRegistrationBean frb = new FilterRegistrationBean();\n\t\tfrb.setFilter(new ForwardedHeaderFilter());\n\t\tfrb.setOrder(Ordered.HIGHEST_PRECEDENCE);\n\t\tfrb.setEnabled(c.isEnabled());\n\t\tfrb.addInitParameter(ForwardedHeaderFilter.ENABLE_RELATIVE_REDIRECTS_INIT_PARAM,\n\t\t\t\tBoolean.toString(c.isEnableRelativeRedirects()));\n\t\tfrb.addInitParameter(ForwardedHeaderFilter.HEADER_PROCESSING_STRATEGY, c.getHeaderProcessingStrategy().name());\n\t\tfrb.addInitParameter(ForwardedHeaderFilter.X_FORWARDED_PREFIX_STRATEGY, c.getXForwardedPrefixStrategy().name());\n\t\treturn frb;\n\t}\n}\n```\nAnd then in application.yml:\n```yml\nde:\n  qaware:\n    xff:\n      enabled: true\n      #enableRelativeRedirects: false\n      #headerProcessingStrategy: EVAL_AND_KEEP  # EVAL_AND_KEEP, EVAL_AND_REMOVE, DONT_EVAL_AND_REMOVE , or disable the filter with enabled: false\n      #xForwardedPrefixStrategy: PREPEND # one of: PREPEND, REPLACE\n```\n## web.xml (e.g. Websphere Liberty or Spring)\n```xml\n\u003c!--ForwardedHeaderFilter MUST be first filter in chain --\u003e\n\u003cfilter\u003e\n    \u003cfilter-name\u003eForwardedHeaderFilter\u003c/filter-name\u003e\n    \u003cfilter-class\u003ede.qaware.xff.filter.ForwardedHeaderFilter\u003c/filter-class\u003e\n    \u003cinit-param\u003e\n        \u003cparam-name\u003eheaderProcessingStrategy\u003c/param-name\u003e\n        \u003cparam-value\u003eEVAL_AND_REMOVE\u003c/param-value\u003e\n    \u003c/init-param\u003e\n    \u003cinit-param\u003e\n        \u003cparam-name\u003exForwardedPrefixStrategy\u003c/param-name\u003e\n        \u003cparam-value\u003eREPLACE\u003c/param-value\u003e\n    \u003c/init-param\u003e\n    \u003c!-- \n    \u003cinit-param\u003e\n        \u003cparam-name\u003eenableRelativeRedirects\u003c/param-name\u003e\n        \u003cparam-value\u003efalse\u003c/param-value\u003e\n    \u003c/init-param\u003e\n    --\u003e\n\u003c/filter\u003e\n\u003cfilter-mapping\u003e\n    \u003cfilter-name\u003eForwardedHeaderFilter\u003c/filter-name\u003e\n    \u003curl-pattern\u003e/*\u003c/url-pattern\u003e\n\u003c/filter-mapping\u003e\n```\n\n## Disable other (x-)forwarded* header processing in various products\n### Websphere liberty\nDisable [trustedHeaderOrigin](https://www.ibm.com/support/knowledgecenter/beta/SSEQTJ_8.5.5/com.ibm.websphere.wlp.nd.doc/ae/rwlp_config_httpDispatcher.html#rwlp_config_httpDispatcher__trustedHeaderOrigin) inside server.xml \n`\u003chttpDispatcher trustedHeaderOrigin=\"none\" /\u003e`\n\n### Spring\n 1. Don't register the org.springframework.web.filter.ForwardedHeaderFilter\n 2. Set `server.use-forward-headers=false` - generically turns off the forward header handling in the underlying webserver! (e.g. tomcat's RemoteIpValve) see: [\"howto-use-tomcat-behind-a-proxy-server\"](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-embedded-servlet-containers.html#howto-use-tomcat-behind-a-proxy-server\")\n\n### Tomcat\nDon't register RemoteIpValve in TomcatEmbeddedServletContainerFactory\n```java\nvoid configureTomcatXForwardedHandling(TomcatEmbeddedServletContainerFactory factory){\n  if(xForwardedHandlingByTomcat){\n\tRemoteIpValve remoteIpValve = new RemoteIpValve();\n\tfactory.addContextValves(remoteIpValve);\n  }\n}\t \n```  \n\n# What? Why? X-forwarded?\n\n## Why do I need (x-)forwarded\n\n1. Imagine your applications sits behind a proxy or another serivce or a chain of proxies/services\n2. Imagine your application is reachable over different DNS names \n3. You never want to hardcode external URL in the backend service -  ever!\n\nNow you need, for whatever reason, the exact external URL as the client calling you sees it. \nFor example, you use an generic Login Proxy 'login.corp.com' in front of you business services e.g. 'biz.corp.com'. And you require 'login.corp.com' to send your user back to your service, after he's done authenticating. For this your business backend service  lets call it 'biz.int.corp' (note: internal service URL), needs to tell login.corp.com the exact external URL 'biz.corp.com' your user came from. You do NOT want to hardcode the external URL in your internal service - ever! - it Kills a/b testing and you cannot expose the same service via different URLS. Its just a knightmare. But lets continue. To forward a user back to you, your backend service now needs to dyamically know the protocol, host, port and maybe the prefix form `HttpServletRequest` to reconstruct the original URL - as your client connecting via the internet sees it.\n\nWithout a mechanism aware of \"forwarded\" headers your `HttpServletRequest` does only contain the protocol, host and port of the service/proxy __infront__ of your service - which probably is some company internal DNS address and port.\nFurthermore the service/proxy in front of you may strips some prefix form your request path, for example because it aggregates \u0026 maps multiple bakcend services behind a single domain\n\n\nTo support this usecase of forwarding the external URL information to backend services, many proxies and webservers stated supporing various (x-)forwarded* headers.\nUnfortunately almost all have very bad support for these (pseudo-)standard forwarded headers, implement only parts, implement them poorly or lack the support completely.\nSee:  [(x-)forwarded* support in various products](#x-forwarded-support-in-various-products)\n\nThis filter will transparently take care of these concerns for you, by wrapping the `HttpServletRequest` which will overwrite various methods to return the correct information.\n \n \n## Why should i use THIS filter\n \nBecause most libraries and webservers have no, very bad or lacking support for these headers.\nThe best filter i could find was the [Spring-Web ForwardedHeaderFilter](https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java).\nThis implementation is based on the filter from Spring.\n\nThen why not use the Spring filter? \n  - because it requires the the full blown spring-web  dependency  - fine if your are already using Spring, but overkill in a small microservice just requiring this filter\n  - it lacks support to PREPEND the value in 'X-Forwarded-Prefix' instead of REPLACE it -  is crucial for us, as we use this filter in a proxy and need to pass the values downstream\n  - more differences: [Implementation details](#implementation-details)\n  \n  \n## What this filter is not\n- no support for \"client identification\" with x-forwarded-for\n\n\n\n## (x-)forwarded* support in various products\n\n|                                           | this filter | Spring 5.0.4 | Tomcat | IBM Websphere Liberty | Jetty | Apache mod_proxy                  | nginx | \n| ----------------------------------------- |------------ | ------------ | ------ | --------------------- | ----- | --------------------------------- | ------| \n| Forwarded                                 | YES         | YES          | ?      | NO                    | YES   | NO(manually with rewrite engine?) | manually with custom proxy_set_header  | \n| X-Forwarded-Proto                         | YES         | YES          | YES    | YES                   | YES   | NO(manually with rewrite engine?) | manually with custom proxy_set_header  | \n| X-Forwarded-Host                          | YES         | YES          | YES*1) | NO                    | YES   | YES                               | manually with custom proxy_set_header  | \n| X-Forwarded-Port                          | YES         | YES          | YES    | NO                    | NO    | NO(manually with rewrite engine?) | manually with custom proxy_set_header  | \n| X-Forwarded-Prefix                        | YES         | YES          | NO     | NO                    | NO    | NO(manually with rewrite engine?) | manually with custom proxy_set_header  | \n| X-Forwarded-By                            | NO          | NO           | YES    | NO                    | NO    | NO(manually with rewrite engine?) | manually with custom proxy_set_header  | \n| X-Forwarded-For                           | NO          | NO           | YES    | NO                    | YES   | YES                               | YES | \n| X-Forwarded-Server                        | NO          | NO           | NO     | NO                    | YES   | YES                               | manually with custom proxy_set_header  | \n| X-Real-IP                                 | NO          | NO           | NO     | NO                    | NO    | NO(manually with rewrite engine?) | YES | \n| X-Proxied-Https                           | NO          | NO           | NO     | NO                    | YES   | NO(manually with rewrite engine?) | manually with custom proxy_set_header  | \n| X-Forwarded-SSL                           | NO          | NO           | YES    | NO                    | NO    | NO?                               | manually with custom proxy_set_header  | \n| Supports COMMA+SPACE separated values     | YES         | YES          | NO     | NO                    | YES   | ?                                 | ? |\n| Supports multiple Headers with same name  | YES         | YES          | YES    | NO                    | NO    | ?                                 | ? | \n| Strip forwarded header from `Request`     | YES(toggle) | YES(always)  | ?      | ?                     | ?     | ?                                 | ? | \n| Supports relative redirects in `Response` | YES         | YES          | NO     | NO                    | NO    | ?                                 | ? |\n| `X-Forwarded-Prefix` processing strategy  | PREPEND or REPLACE | REPLACE | ?    | ?                     | ?     | ?                                 | ? | \n\n*1) [x-forwarded-host is supported since 9.0.23,8.5.44,7.0.97](https://bz.apache.org/bugzilla/show_bug.cgi?id=57665)\n\n\n## X-Forwarded headers overview and explanation\n\n| Header                 | Description |\n| ---------------------- | ----------- |\n| Forwarded              | same function as x-forwarded-{proto,host,port} [see RFC 7239](http://tools.ietf.org/html/rfc7239) |\n| X-Forwarded-Proto      | The original protocol requested by the client in the Host HTTP request header |\n| X-Forwarded-Host       | The original host requested by the client in the Host HTTP request header |\n| X-Forwarded-Port       | The original port requested by the client in the Host HTTP request header |\n| X-Forwarded-Prefix     | If a proxy strips a prefix from the path before forwarding it might add an X-Forwarded-Prefix header |\n| X-Forwarded-By         | Name of the http header created by this valve to hold the list of proxies that have been processed in the incoming remoteIpHeader |\n| X-Forwarded-For        | The IP address of the client |\n| X-Forwarded-Server     | The hostname of the proxy server |\n| X-Real-IP              | nginx synonym for x-forwarded-for |\n| X-Proxied-Https        | ? |\n| X-Forwarded-SSL        | ? |\n\n\n## Developer Info\n\n### Implementation Details\nBased on [Springs ForwardedHeaderFilter](https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java) but\n - without Spring dependency -\u003e easily integrable into many projects\n - has toogle to NOT remove the evaluated headers from the request \n   - allows using this filter inside a Proxy to forward/append to these headers to downstream services (e.g. Zuul)\n - Selectable processing strategy for `X-Forwarded-Prefix`  \n   - `PREPEND` the Context-Path of the Application with the (first) value from `X-forwarded-Prefix`\n     -  example:\n   - `REPLACE` the Context-Path of the Application with the (first) value from `X-forwarded-Prefix`\n     -  example:\n \n### How to build \nBased on Gradle. First trigger of build will take longer and download the buildtool.\n\n```bash\ngradlew build\n```\n\n# Appendix\n\n## Maintainer\nMichael Frank, \u003cmichael.frank@qaware.de\u003e\n\n## Credits\nCode was taken from Spring 5.0.4 \nThanks to all Contributors who made that possible.\n\n## License\nThis software is provided under the Apache2.0 open source license, read the `LICENSE` file for details.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqaware%2Fx-forwarded-filter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqaware%2Fx-forwarded-filter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqaware%2Fx-forwarded-filter/lists"}