You cannot use a combinator to target a pseudo-element relative to elements other than its generating element.
This is because they're pseudo-elements, not actual elements, and combinators only work by establishing relationships between actual elements. A pseudo-element, on the other hand, can only be applied to the subject of a selector (the rightmost compound selector), and this happens only after matching is processed on the real elements. In other words, matching is done first as though the pseudo-element wasn't there, then the pseudo-element, if it's indicated within the selector, is applied to each match.
In your code, the following selector:
a[href^="http"] img ~ :after
Does not actually look for an :after pseudo-element that comes after an img within the a, even though it appears that way as both are rendered as children of the a element.
It can be rewritten into the following:
a[href^="http"] img ~ *:after
Notice the * selector, which is implied. Similarly to how you can omit * before any other simple selectors for it to be implied, omitting * from a pseudo-element also makes it implied to be there. See the spec for details.
Now, even though it appears *:after should still match a:after (since a would match *), it still doesn't work that way. If you remove the :after pseudo-element from the selector:
a[href^="http"] img ~ *
You'll notice that the meaning of the selector changes entirely:
Select any element
  that appears as a following sibling of an img
  that is a descendant of an a (whose href starts with "http").
Since the img is the last child of the a element in your HTML, there are no following siblings to match, and therefore no :after pseudo-elements can be generated.
In the case of a :before or :after pseudo-element, one might think of matching the pseudo-element's generating element relative to the pseudo-element's "sibling", but as the OP has correctly pointed out, there is no parent selector, so they're out of luck there, too.