Here's a one line solution.

Put this in your `functions.php` file: 

```php
add_action( 'template_redirect', function () {
    ob_start();
} );
```

`ob_start()` begins output buffering, when the `template_redirect` actions are completed, and nothing has been sent to the browser. That means `wp_redirect()` works from anywhere:

```php
wp_redirect( $url );
exit;
```

including in a shortcode. If the script terminates normally, without an exit, the buffer is automatically flushed and the whole page is sent at that time.

Cheeky ... but it seems to work for me.