Quick answer: Controls follow the window only if their anchors are relative to something that resizes. Use a root Control set to the Full Rect preset under a CanvasLayer, lay out children with Containers rather than fixed positions, and pick a Stretch Mode (Project Settings > Display > Window > Stretch) that matches your game.

The UI looks right at the size you designed it, then a player maximises the window and the health bar is stuck in the middle of the screen or the menu overflows. Godot has two separate systems involved — window stretching and Control anchoring — and both have to be set up.

Step 1: choose a stretch mode

Project Settings > Display > Window > Stretch > Mode:

Set Aspect to expand if you want extra space on wider screens instead of black bars, then design anchors so the UI uses that extra space sensibly. Check that Display > Window > Size > Resizable is on if players should be able to resize.

Step 2: anchor to a parent that resizes

Anchors are relative to the parent Control's rectangle. A Control whose parent is not a Control (for example a Node2D) has no sensible rectangle to follow, and it also moves with the camera. Build HUDs like this:

HUD (CanvasLayer)
└── Root (Control, Layout preset: Full Rect)
    ├── TopBar (HBoxContainer, preset: Top Wide)
    └── Minimap (TextureRect, preset: Bottom Right)

Apply presets from the Layout menu in the toolbar (the anchor icon) rather than dragging nodes into place: dragging changes offsets but leaves anchors at the top-left.

Step 3: let containers do the layout

Inside a Container, children's anchors and positions are controlled by the container, so edits to them appear to do nothing. Use Size Flags instead: Fill and Expand make a child grow into available space, and size_flags_stretch_ratio shares space between siblings. Use MarginContainer for padding instead of manual offsets.

Step 4: check minimum sizes

A Control never shrinks below its custom_minimum_size or its content's minimum size (long Label text, large textures). If a panel will not get smaller, look for a child with a large minimum size, and enable autowrap_mode on long labels.

Reacting to size changes in code

If you position things manually, update them when the viewport changes rather than once in _ready():

func _ready():
    get_viewport().size_changed.connect(_layout)
    _layout()

func _layout():
    var size := get_viewport_rect().size
    $Crosshair.position = size / 2 - $Crosshair.size / 2

Test with the editor's Run > Window size options or by resizing the running game, at a few extreme aspect ratios such as 4:3, 16:9 and 21:9.

Related fixes