What is the fastest way of combining elements of lists in rules?

L1 = {{a, b}, {c, d}};
L2 = {{e, f}, {g, h}};

L1r = ArrayReshape[L1, {Times @@ Most[#], Last[#]}] &[Dimensions[L1]];
L2r = ArrayReshape[L2, {Times @@ Most[#], Last[#]}] &[Dimensions[L2]];

new = Transpose[Tuples[{L1r, L2r}], {1, 3, 2}];
new[[All, All, 0]] = Rule;

new
{{a -> e, b -> f}, {a -> g, b -> h}, {c -> e, d -> f}, {c -> g, d -> h}}

One straightforward way is with AssociationThread.

keys = {{a, b}, {c, d}}
values = {{e, f}, {g, h}}

AssociationThread @@@ Tuples[{keys, values}]

(* {<|a -> e, b -> f|>, <|a -> g, b -> h|>, 
    <|c -> e, d -> f|>, <|c -> g, d -> h|>} *)

And for the second example:

keys = {{a, b}}
values = {{e, f}, {g, h}}

AssociationThread @@@ Tuples[{keys, values}]

(* {<|a -> d|>, <|a -> f|>, <|b -> d|>, <|b -> f|>} *)

Of course, you end up with a list of Associations, but is that such a bad thing? You can always turn it into a list with Normal.

As for the second example, it clearly doesn't meet your "lists need not be of the same dimensions" specification. You can either take the one liner and use it on keys = {{a, b}} instead of {a, b}, or you can use this longer function, but get the functionality you're after:

listtorules[list1_, values_] := 
 Block[{keys = If[Depth[list1] == 2, {list1}, list1]},
  Normal[AssociationThread @@@ Tuples[{keys, values}]]
  ]

Then

listtorules[{a, b}, {{c, d}, {e, f}}]

listtorules[{{a, b}}, {{c, d}, {e, f}}]

both produce

{{a -> c, b -> d}, {a -> e, b -> f}}

Use Outer:

lst01 = {{a, b}, {c, d}}
lst02 = {{e, f}, {g, h}}
Outer[Thread@*Rule, lst01, lst02, 1]

You'll have to use {{a,b}} instead of {a,b} for your second case. And if you really need not have a matrix of results, you can Catenate the results of the above (but of course that has a cost).