Packages
plug
1.20.2
1.20.3
1.20.2
1.20.1
retired
1.20.0
retired
1.19.5
1.19.4
1.19.3
1.19.2
1.19.1
1.19.0
1.18.5
1.18.4
1.18.3
1.18.2
1.18.1
1.18.0
1.17.4
1.17.3
1.17.2
1.17.1
1.17.0
1.16.6
1.16.5
1.16.4
1.16.3
1.16.2
1.16.1
1.16.0
1.15.6
1.15.5
1.15.4
1.15.3
1.15.2
1.15.1
1.15.0
1.14.2
1.14.1
1.14.0
1.13.6
1.13.5
1.13.4
1.13.3
1.13.2
1.13.1
retired
1.13.0
retired
1.12.1
1.12.0
1.11.1
1.11.0
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.0
1.8.3
1.8.2
1.8.1
1.8.0
1.7.2
1.7.1
1.7.0
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.5.0-rc.2
1.5.0-rc.1
1.5.0-rc.0
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.0
1.3.6
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.14.0
0.13.1
0.13.0
0.12.2
0.12.1
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.0
0.6.0
0.5.3
0.5.2
0.5.1
0.5.0
0.4.4
0.4.3
0.4.2
0.4.1
Compose web applications with functions
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/plug/rewrite_on.ex
defmodule Plug.RewriteOn do
@moduledoc """
A plug to rewrite the request's host/port/protocol from `x-forwarded-*` headers.
If your Plug application is behind a proxy that handles HTTPS, you may
need to tell Plug to parse the proper protocol from the `x-forwarded-*`
header.
plug Plug.RewriteOn, [:x_forwarded_host, :x_forwarded_port, :x_forwarded_proto]
The supported values are:
* `:x_forwarded_for` - to override the remote IP based on the "x-forwarded-for" header
* `:x_forwarded_host` - to override the host based on the "x-forwarded-host" header
* `:x_forwarded_port` - to override the port based on the "x-forwarded-port" header
* `:x_forwarded_proto` - to override the protocol based on the "x-forwarded-proto" header
Some HTTPS proxies use nonstandard headers, which can be specified in the list via tuples:
* `{:remote_ip, header}` - to override the remote IP based on a custom header
* `{:host, header}` - to override the host based on a custom header
* `{:port, header}` - to override the port based on a custom header
* `{:scheme, header}` - to override the protocol based on a custom header
A tuple representing a Module-Function-Args can also be given as argument
instead of a list.
Since rewriting the scheme based on `x-forwarded-*` headers can open up
security vulnerabilities, only use this plug if:
* your app is behind a proxy
* your proxy strips the given `x-forwarded-*` headers from all incoming requests
* your proxy sets the `x-forwarded-*` headers and sends it to Plug
"""
@behaviour Plug
import Plug.Conn, only: [get_req_header: 2]
@impl true
def init({_m, _f, _a} = header), do: header
def init(header), do: List.wrap(header)
@impl true
def call(conn, [:x_forwarded_for | rewrite_on]) do
call(conn, [{:remote_ip, "x-forwarded-for"} | rewrite_on])
end
def call(conn, [:x_forwarded_proto | rewrite_on]) do
call(conn, [{:scheme, "x-forwarded-proto"} | rewrite_on])
end
def call(conn, [:x_forwarded_port | rewrite_on]) do
call(conn, [{:port, "x-forwarded-port"} | rewrite_on])
end
def call(conn, [:x_forwarded_host | rewrite_on]) do
call(conn, [{:host, "x-forwarded-host"} | rewrite_on])
end
def call(conn, [{:remote_ip, header} | rewrite_on]) do
conn
|> put_remote_ip(get_req_header(conn, header))
|> call(rewrite_on)
end
def call(conn, [{:scheme, header} | rewrite_on]) do
conn
|> put_scheme(get_req_header(conn, header))
|> call(rewrite_on)
end
def call(conn, [{:port, header} | rewrite_on]) do
conn
|> put_port(get_req_header(conn, header))
|> call(rewrite_on)
end
def call(conn, [{:host, header} | rewrite_on]) do
conn
|> put_host(get_req_header(conn, header))
|> call(rewrite_on)
end
def call(_conn, [other | _rewrite_on]) do
raise "unknown rewrite: #{inspect(other)}"
end
def call(conn, []) do
conn
end
def call(conn, {mod, fun, args}) do
call(conn, apply(mod, fun, args))
end
defp put_scheme(%{scheme: :http, port: 80} = conn, ["https"]),
do: %{conn | scheme: :https, port: 443}
defp put_scheme(%{scheme: :http, port: 80} = conn, ["wss"]),
do: %{conn | scheme: :https, port: 443}
defp put_scheme(conn, ["https"]),
do: %{conn | scheme: :https}
defp put_scheme(conn, ["wss"]),
do: %{conn | scheme: :https}
defp put_scheme(%{scheme: :https, port: 443} = conn, ["http"]),
do: %{conn | scheme: :http, port: 80}
defp put_scheme(conn, ["http"]),
do: %{conn | scheme: :http}
defp put_scheme(conn, _scheme),
do: conn
defp put_host(conn, [proper_host]),
do: %{conn | host: proper_host}
defp put_host(conn, _),
do: conn
defp put_port(conn, headers) do
with [header] <- headers,
true <- byte_size(header) <= 5,
{port, ""} <- Integer.parse(header) do
%{conn | port: port}
else
_ -> conn
end
end
defp put_remote_ip(conn, headers) do
with [header] <- headers,
[client | _] <- :binary.split(header, ","),
{:ok, remote_ip} <- :inet.parse_address(String.to_charlist(client)) do
%{conn | remote_ip: remote_ip}
else
_ -> conn
end
end
end