Here’s something that worked for us: Instead of trying to manipulate or initialize the history, we do a super-quick jump between states to ensure that the ‘back’ state is always available and always the page we want. The ‘jump’ does not seem to be visible on any device (that I’ve tested, so far). I don’t know if this solution will work generally, but it fit my app’s needs.

The general idea:
In the state’s onEnter, we examine the history stack – and if the relevant history item there isn’t the page we want for our back button, we jump (without animation) to that page and then immediately return to the original page.

Edit: Apparently this does not work so well on iOS: the ion-nav-back-button is present, but it doesn’t appear unless you tap the ion-nav-bar.

$stateProvider.state('myHomeState', {
/* url, views, data, etc. */
onEnter: ['$state', '$ionicViewService', function($state, $ionicViewService) {
var viewHistory = $ionicViewService._getHistory();
var lastStateInHistory, disableAnimation;
if (viewHistory.cursor === -1) {
// Simple case: we're at the end of the history stack (or else there is no history stack)
lastStateInHistory = _.last(viewHistory.stack);
disableAnimation = true;
} else if (viewHistory.cursor < viewHistory.stack.length) {
// Valid cursor: this is just like the above case, except we're in the middle of the
// history stack instead of at the end
lastStateInHistory = viewHistory.stack[viewHistory.cursor];
disableAnimation = true;
} else {
// Invalid cursor: this can happen after a navClear or disableBack. We want to treat it
// just like the first simple case, except if we suppress the animation here then we won't
// see an animation if we subsequently leave and then come back to this page.
lastStateInHistory = _.last(viewHistory.stack);
disableAnimation = false;
}
if (!lastStateInHistory || lastStateInHistory.stateId !== 'myBackState') {
// Our history is missing or wrong: let's transition between states to fix it.
// (We don't want the end user to see this transition, though, so suppress its animation)
$ionicViewService.nextViewOptions({
disableAnimate: true
});
$state.go('myBackState').then(function() {
// We *usually* want to suppress this second animation, unless it's the "invalid cursor"
// case above.
if (disableAnimation) {
$ionicViewService.nextViewOptions({
disableAnimate: true
});
}
$state.go('myHomeState');
});
}
}]
});