I have code to steer a user away from a URL that they shouldn’t have access to (my ‘protector’ code).
My code was using
redirect(to: "/") which turned out to be unreliable (the user remained on the forbidden target page instead of redirecting the user back to the home page). Sometimes it seemed to work, other times it didn’t!?
Apparently, when the docs say that the to: parameter is a ‘relative URL’, they really mean it! It seems to be relative to the current URL instead of the website root URL.
Example: if the target URL is http://mysite.com/dangerous_data/5/edit, then the relative URL of “/” just redirects the user to http://mysite.com/dangerous_data/5/edit/ instead of http://mysite.com/.
To get the user back to the home page, I had to add a route that specified
get("/index", PageController, :index) and use
redirect(to: "/index") in my ‘website protector’ code.
My guess is you have a plug for the auth check where you neglected to call
halt/1 after the call to redirect, which would allow the plug pipeline to continue down to the controller action. The following should work as you want:
def my_auth_plug(conn, _) do
if ... do
|> redirect(to: "/")
Thanks for the tip!
I wasn’t using a plug but your solution looks clean and a better idea than what I’m using.
For posterity, can you share your previous code? It’s also sounds like you may have returned the wrong
conn binding so your redirect call had no effect in the controller.
Sure! BTW: It looks like I was using a plug!
defmodule PtrackWeb.Plugs.SetUser do
def init(_params) do
def call(conn, _params) do
# Do other things
defmodule PtrackWeb.AuthController do
use PtrackWeb, :controller
def isLocal(conn) do
host = List.first(get_req_header(conn, "host"))
host =~ "localhost" || host =~ "127.0.0.1"
def check_access(conn) do
# Check if they are trying to access a page that can muck up the data
# If they are and they are not running on localhost, interrupt their mischief!
unsafe = Enum.member?(["upload", "edit", "update", "delete", "new", "create"],
if(unsafe && !isLocal(conn)) do
|> put_flash(:danger, "Hackers need not apply!")
|> redirect(to: "/")
defmodule PtrackWeb.Router do
use PtrackWeb, :router
pipeline :browser do
plug(PtrackWeb.Plugs.SetUser) #checks that the page is safe for them to fetch
pipeline :auth do
scope "/", PtrackWeb do
# Typical router code like:
I think that’s all of the relevant code.
I was testing the URL “http://thesite.com/tickets/682/edit” so I should have been kicked back to my homepage, which it did, according to the terminal’s debug info, but the edit form remained on screen instead of displaying the home page!?!
Sending the user back to “/index” instead of “/” is what got my system to display the homepage after failing the check_access() test.
The path in this case isn’t the issue. What’s happening is the
SetUser plug fails to
halt the conn, because your
PtrackWeb.AuthController.check_access/1 plug redirects, but doesn’t halt. It may be confusing because you’ll see controllers redirect but not call halt. Controllers are the end of the line as far as the plug pipeline goes, so there is nothing downstream to halt from, but when you redirect (or send a response) upstream of the controller via a plug, you must
halt to prevent downstream plugs from being called. I think the “/index” vs “/” is a fluke in this case.
Thanks! Now I understand what you are saying after I carefully re-read your comment.