Short answer
You should use RuleDelayed:
"foo" /. x_String :> Head[x]
Othewise, the r.h.s of your rule is computed before the match, when x is still a symbol, not bound to anything.
Longer answer
Let's enumerate the main steps the main evaluator goes through, when evaluating your original code.
The case of Rule
The head ReplaceAll is evaluated (to itself, trivially). Evaluation then goes to the elements (arguments of ReplaceAll), left to right.
The first argument, "foo", is trivially evaluated to itself
The second argument, Rule[x_String, Head[x]] is evaluated
The head Rule is trivially evaluated.
Both of its arguments are evaluated, since Rule does not hold arguments
x_String is evaluated to itself
Head[x] is evaluated to Symbol, since at this time, in your case, x was not assigned any value. If it was, Head[x] would have evaluated to the head of that value.
Finally, the internal DownValues for ReplaceAll are allowed to fire, "on the way up" in the recursive evaluation procedure. By this time, the rule you want to apply, is
x_String -> Symbol
And thus the result.
The case of RuleDelayed
The head ReplaceAll is evaluated (to itself, trivially). Evaluation then goes to the elements (arguments of ReplaceAll), left to right.
The first argument, "foo", is trivially evaluated to itself
The second argument, RuleDelayed[x_String, Head[x]] is evaluated
The head RuleDelayed is trivially evaluated.
Arguments of RuleDelayed start to evaluate from left to right. However, RuleDelayed is HoldRest, and that is important here.
x_String is evaluated to itself
Head[x] is not evaluated at this time, due to the HoldRest attribute of enclosing RuleDelayed.
The internal DownValues for ReplaceAll are allowed to fire, "on the way up" in the recursive evaluation procedure. By this time, the rule you want to apply, is
x_String :> Head[x]
Now, ReplaceAll is in the position to bind the value of x on the r.h.s. with the result found during the pattern-matching of the pattern on the l.h.s. against your expression ("foo" in this case). This results in a new expression Head["foo"]
Finally, Head["foo"] evaluates to String.
Can we get the right result with Rule?
To answer your specific question in comments, it also should be clear from the above discussion, that generally one should use RuleDelayed to get the right result, instead of Rule.
One thing you can do, if you insist on using Rule, is the following:
"foo" /. Unevaluated[x_String -> Head[x]]
(* String *)
This works in this case, but note that in this approach, x_String does not evaluate either. This is Ok here, but it is important to note the difference.
Summary
There are just a few things one needs to remember about Rule vs RuleDelayed:
So in general, when your patterns contain variables, it is RuleDelayed you typically want to use, not Rule.
RuleDelayed:"foo" /. x_String :> Head[x]. Othewise, the r.h.s of your rule is computed before the match, whenxis still a symbol, not bound to anything. – Leonid Shifrin May 15 '16 at 22:31->) rule determine the "stringiness" of a particular symbol (i.e. the fact that it's quoted)? BTW, why don't you make this an answer? – Adrian May 15 '16 at 23:27xhas no connection to the string"foo"whatsoever, until the rule application happens. – Leonid Shifrin May 16 '16 at 11:19RulevsRuleDelayed, perhaps (22917) or (24860). – Mr.Wizard May 16 '16 at 11:26