The issue is that some characters are actually left of the anchor point (like T, Y, V, O, etc.) compared to more square characters (like H, D, R, etc). To fix that, you can use the sourceRectAtTime() ‘left’ attribute which can give you that left-shift value relative to the anchor point.
Here’s my typical process for these (assuming left justified text and scale is 100%, 100%):
Create a text layer and type something in it.
Then, without a layer selected, double click the rectangular mask/path button to make a comp-sized rectangular shape layer that has it’s anchor point in the center (that’s the quickest way I’ve found to do that), parent that shape layer to the text layer and move it below the text layer (set color for the shape layer as needed).
Then for the contents>rectangle>rectangle path>size property, I use this expression:
hPad = 10 ;
vPad = 10 ;
rect = parent.sourceRectAtTime() ;
[ rect.width + rect.left, rect.height] + 2 * [ hPad, vPad ]
And for contents>rectangle>rectangle path>position property, I use this expression:
rect = parent.sourceRectAtTime() ;
[ rect.width + rect.left, -rect.height] / 2
That should keep the left edge relative to the anchor point of the text layer.
I should note that I also often hardcode the height in both of those expressions to prevent issues with descenders or other character height variations… Even when fonts are all caps, there can be issues with round characters (like O vs H ), Q’s often go below the baseline and some punctuation can create problems (like commas). That can take a bit of fiddling, since the position is about 1/3 of the height… I could probably go further with the expression and use the sourceText.style to derive the baseline and fontSize…