To fully understand this answer one must know about DownValues, the documentation is a good place to start reading about this, as well as (96).
In your situation, it is better to use = instead of delayed assignment :=. Let's take a look what this means for the function definition:
f[x_] := a /. {a -> x};
DownValues[f]
{HoldPattern[f[x_]] :> (a /. {a -> x})}
f[x_] = a /. {a -> x};
DownValues[f]
{HoldPattern[f[x_]] :> x}
The function definitions should always give the same result, even though in the first case the replacement happens at runtime and in the second case the replacement happens when the function is defined.
If you use the second way you will have no problem writing the code the way you want.
rule = {a -> x};
f[x_] = a /. rule;
DownValues[f]
{HoldPattern[f[x_]] :> x}
The function gives the same result as both of the previous cases described, and the definition of the function is equivalent to the second case.
Hopefully, you now understand how to figure out why it does not work with :=. Again, we use DownValues which shows us the definition of a given symbol:
rule = {a -> x};
f[x_] := a /. rule;
DownValues[f]
{HoldPattern[f[x_]] :> (a /. rule)}
This tells us that the way the function f is being implemented in Mathematica is as a replacement rule, like this:
f[1] /. HoldPattern[f[x_]] :> (a /. rule)
x in the pattern does not appear on the right-hand side of the rule, that's why it isn't working. Symbols implicitly defined on the left-hand side of the rule must appear explicitly on the right-hand side of the rule.
Trace[f[1]]in both of your possibilities? – J. M.'s missing motivation Mar 18 '17 at 20:21